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,467 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
// Category : Helpers
// Filename : public.sdk/source/vst/utility/dataexchange.cpp
// Created by : Steinberg, 06/2023
// Description : VST Data Exchange API 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 "dataexchange.h"
#include "public.sdk/source/vst/utility/alignedalloc.h"
#include "public.sdk/source/vst/utility/ringbuffer.h"
#include "base/source/timer.h"
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include <string_view>
#ifdef _MSC_VER
#include <malloc.h>
#endif
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
static constexpr DataExchangeBlock InvalidDataExchangeBlock = {nullptr, 0,
InvalidDataExchangeBlockID};
//------------------------------------------------------------------------
bool operator== (const DataExchangeHandler::Config& c1, const DataExchangeHandler::Config& c2)
{
return c1.userContextID == c2.userContextID && c1.alignment == c2.alignment &&
c1.numBlocks == c2.numBlocks && c1.blockSize == c2.blockSize;
}
static constexpr auto MessageIDDataExchange = "DataExchange";
static constexpr auto MessageIDQueueOpened = "DataExchangeQueueOpened";
static constexpr auto MessageIDQueueClosed = "DataExchangeQueueClosed";
static constexpr auto MessageKeyData = "Data";
static constexpr auto MessageKeyBlockSize = "BlockSize";
static constexpr auto MessageKeyUserContextID = "UserContextID";
//------------------------------------------------------------------------
struct MessageHandler : ITimerCallback
{
using RingBuffer = OneReaderOneWriter::RingBuffer<void*>;
IPtr<Timer> timer;
IPtr<IHostApplication> hostApp;
IConnectionPoint* connection {nullptr};
RingBuffer realtimeBuffer;
RingBuffer messageBuffer;
RingBuffer rtOnlyBuffer;
void* lockedRealtimeBlock {nullptr};
DataExchangeHandler::Config config {};
MessageHandler (FUnknown* hostContext, IConnectionPoint* connection) : connection (connection)
{
hostApp = U::cast<IHostApplication> (hostContext);
}
~MessageHandler () noexcept override
{
if (timer)
timer->stop ();
timer = nullptr;
}
bool openQueue (const DataExchangeHandler::Config& c)
{
if (!hostApp || !connection)
return false;
config = c;
timer = owned (Timer::create (this, 1));
if (!timer)
return false;
realtimeBuffer.resize (config.numBlocks);
messageBuffer.resize (config.numBlocks);
rtOnlyBuffer.resize (config.numBlocks);
for (auto i = 0u; i < config.numBlocks; ++i)
{
auto data = aligned_alloc (config.blockSize, config.alignment);
realtimeBuffer.push (data);
}
if (auto msg = owned (allocateMessage (hostApp)))
{
msg->setMessageID (MessageIDQueueOpened);
if (auto attr = msg->getAttributes ())
{
attr->setInt (MessageKeyUserContextID, config.userContextID);
attr->setInt (MessageKeyBlockSize, config.blockSize);
}
connection->notify (msg);
}
return true;
}
bool closeQueue ()
{
if (timer)
timer->stop ();
timer = nullptr;
void* data;
while (realtimeBuffer.pop (data))
{
aligned_free (data, config.alignment);
}
while (messageBuffer.pop (data))
{
aligned_free (data, config.alignment);
}
while (rtOnlyBuffer.pop (data))
{
aligned_free (data, config.alignment);
}
if (auto msg = owned (allocateMessage (hostApp)))
{
msg->setMessageID (MessageIDQueueClosed);
if (auto attr = msg->getAttributes ())
{
attr->setInt (MessageKeyUserContextID, config.userContextID);
}
connection->notify (msg);
}
return true;
}
void* lockBlock ()
{
if (lockedRealtimeBlock != nullptr)
return nullptr;
void* data;
if (rtOnlyBuffer.pop (data))
{
lockedRealtimeBlock = data;
return lockedRealtimeBlock;
}
if (realtimeBuffer.pop (data))
{
lockedRealtimeBlock = data;
return lockedRealtimeBlock;
}
return nullptr;
}
bool freeBlock (bool send)
{
if (send)
{
if (messageBuffer.push (lockedRealtimeBlock))
{
lockedRealtimeBlock = nullptr;
return true;
}
return false;
}
if (rtOnlyBuffer.push (lockedRealtimeBlock))
{
lockedRealtimeBlock = nullptr;
return true;
}
return false;
}
void onTimer (Timer* /*_timer*/) override
{
void* data;
while (messageBuffer.pop (data))
{
if (auto msg = owned (allocateMessage (hostApp)))
{
msg->setMessageID (MessageIDDataExchange);
if (auto attributes = msg->getAttributes ())
{
attributes->setInt (MessageKeyUserContextID, config.userContextID);
attributes->setBinary (MessageKeyData, data, config.blockSize);
connection->notify (msg);
}
}
realtimeBuffer.push (data);
}
}
};
//------------------------------------------------------------------------
struct DataExchangeHandler::Impl
{
Config config {};
ConfigCallback configCallback;
IDataExchangeHandler* exchangeHandler {nullptr};
IConnectionPoint* connectionPoint {nullptr};
FUnknown* hostContext {nullptr};
IAudioProcessor* processor {nullptr};
std::unique_ptr<MessageHandler> fallbackMessageHandler;
DataExchangeQueueID queueID {InvalidDataExchangeQueueID};
DataExchangeBlock currentBlock {InvalidDataExchangeBlock};
bool enabled {true};
bool internalUseExchangeManager {true};
bool isOpen () const { return queueID != InvalidDataExchangeQueueID; }
bool openQueue (bool forceUseMessageHandling)
{
if (exchangeHandler && !forceUseMessageHandling)
{
internalUseExchangeManager = true;
return exchangeHandler->openQueue (processor, config.blockSize, config.numBlocks,
config.alignment, config.userContextID,
&queueID) == kResultTrue;
}
internalUseExchangeManager = false;
fallbackMessageHandler = std::make_unique<MessageHandler> (hostContext, connectionPoint);
if (fallbackMessageHandler->openQueue (config))
{
queueID = 0;
return true;
}
fallbackMessageHandler.reset ();
return false;
}
void closeQueue ()
{
if (queueID == InvalidDataExchangeQueueID)
return;
if (internalUseExchangeManager)
exchangeHandler->closeQueue (queueID);
else if (fallbackMessageHandler)
{
fallbackMessageHandler->closeQueue ();
fallbackMessageHandler.reset ();
}
currentBlock = InvalidDataExchangeBlock;
queueID = InvalidDataExchangeQueueID;
}
DataExchangeBlock lockBlock ()
{
if (!isOpen ())
return InvalidDataExchangeBlock;
else if (currentBlock.blockID != InvalidDataExchangeBlockID)
return currentBlock;
else if (internalUseExchangeManager)
{
auto res = exchangeHandler->lockBlock (queueID, &currentBlock);
if (res != kResultTrue)
currentBlock = InvalidDataExchangeBlock;
return currentBlock;
}
else if (fallbackMessageHandler)
{
if (auto data = fallbackMessageHandler->lockBlock ())
{
currentBlock.data = data;
currentBlock.size = config.blockSize;
currentBlock.blockID = 0;
return currentBlock;
}
}
return InvalidDataExchangeBlock;
}
bool freeBlock (bool send)
{
if (!isOpen () || currentBlock.blockID == InvalidDataExchangeBlockID)
return true;
if (internalUseExchangeManager)
{
auto res = exchangeHandler->freeBlock (queueID, currentBlock.blockID, send);
currentBlock = InvalidDataExchangeBlock;
return res == kResultTrue;
}
else if (fallbackMessageHandler)
{
if (fallbackMessageHandler->freeBlock (send))
{
currentBlock = InvalidDataExchangeBlock;
return true;
}
}
return false;
}
};
//------------------------------------------------------------------------
DataExchangeHandler::DataExchangeHandler (IAudioProcessor* processor)
{
impl = std::make_unique<Impl> ();
impl->processor = processor;
}
//------------------------------------------------------------------------
DataExchangeHandler::DataExchangeHandler (IAudioProcessor* processor, ConfigCallback&& callback)
: DataExchangeHandler (processor)
{
impl->configCallback = std::move (callback);
}
//------------------------------------------------------------------------
DataExchangeHandler::DataExchangeHandler (IAudioProcessor* processor,
const ConfigCallback& callback)
: DataExchangeHandler (processor)
{
impl->configCallback = callback;
}
//------------------------------------------------------------------------
DataExchangeHandler::~DataExchangeHandler () noexcept
{
}
//------------------------------------------------------------------------
void DataExchangeHandler::onConnect (IConnectionPoint* other, FUnknown* hostContext)
{
impl->connectionPoint = other;
impl->hostContext = hostContext;
impl->exchangeHandler = U::cast<IDataExchangeHandler> (hostContext);
}
//------------------------------------------------------------------------
void DataExchangeHandler::onDisconnect (IConnectionPoint* /*other*/)
{
impl->closeQueue ();
impl->connectionPoint = nullptr;
impl->hostContext = nullptr;
impl->exchangeHandler = nullptr;
}
//------------------------------------------------------------------------
void DataExchangeHandler::onActivate (const Vst::ProcessSetup& setup, bool forceUseMessageHandling)
{
Config conf {};
if (impl->configCallback (conf, setup))
{
if (impl->isOpen ())
{
if (impl->config == conf)
return;
impl->closeQueue ();
}
impl->config = conf;
impl->openQueue (forceUseMessageHandling);
}
}
//------------------------------------------------------------------------
void DataExchangeHandler::onDeactivate ()
{
if (impl->isOpen ())
impl->closeQueue ();
}
//------------------------------------------------------------------------
void DataExchangeHandler::enable (bool state)
{
impl->enabled = state;
}
//------------------------------------------------------------------------
bool DataExchangeHandler::isEnabled () const
{
return impl->enabled;
}
//------------------------------------------------------------------------
DataExchangeBlock DataExchangeHandler::getCurrentOrNewBlock ()
{
if (!isEnabled ())
return InvalidDataExchangeBlock;
return impl->lockBlock ();
}
//------------------------------------------------------------------------
bool DataExchangeHandler::sendCurrentBlock ()
{
return impl->freeBlock (true);
}
//------------------------------------------------------------------------
bool DataExchangeHandler::discardCurrentBlock ()
{
return impl->freeBlock (false);
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
struct DataExchangeReceiverHandler::Impl
{
IDataExchangeReceiver* receiver {nullptr};
};
//------------------------------------------------------------------------
DataExchangeReceiverHandler::DataExchangeReceiverHandler (IDataExchangeReceiver* receiver)
{
impl = std::make_unique<Impl> ();
impl->receiver = receiver;
}
//------------------------------------------------------------------------
DataExchangeReceiverHandler::~DataExchangeReceiverHandler () noexcept = default;
//------------------------------------------------------------------------
bool DataExchangeReceiverHandler::onMessage (IMessage* msg)
{
std::string_view msgID = msg->getMessageID ();
if (msgID == MessageIDDataExchange)
{
if (auto attributes = msg->getAttributes ())
{
const void* data;
uint32 sizeInBytes;
if (attributes->getBinary (MessageKeyData, data, sizeInBytes) != kResultTrue)
return false;
int64 userContext;
if (attributes->getInt (MessageKeyUserContextID, userContext) != kResultTrue)
return false;
DataExchangeBlock block;
block.size = sizeInBytes;
block.data = const_cast<void*> (data);
block.blockID = 0;
impl->receiver->onDataExchangeBlocksReceived (static_cast<uint32> (userContext), 1,
&block, false);
return true;
}
}
else if (msgID == MessageIDQueueOpened)
{
if (auto attributes = msg->getAttributes ())
{
int64 userContext;
if (attributes->getInt (MessageKeyUserContextID, userContext) != kResultTrue)
return false;
int64 blockSize;
if (attributes->getInt (MessageKeyBlockSize, blockSize) != kResultTrue)
return false;
TBool backgroundThread = false;
impl->receiver->queueOpened (static_cast<uint32> (userContext),
static_cast<uint32> (blockSize), backgroundThread);
return true;
}
}
else if (msgID == MessageIDQueueClosed)
{
if (auto attributes = msg->getAttributes ())
{
int64 userContext;
if (attributes->getInt (MessageKeyUserContextID, userContext) != kResultTrue)
return false;
impl->receiver->queueClosed (static_cast<uint32> (userContext));
return true;
}
}
return false;
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg