123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- /*
- * BridgeCommon.h
- * --------------
- * Purpose: Declarations of stuff that's common between the VST bridge and bridge wrapper.
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #pragma once
- #include "openmpt/all/BuildSettings.hpp"
- #include <vector>
- #if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
- #include <intrin.h>
- #endif
- OPENMPT_NAMESPACE_BEGIN
- // Insert some object at the end of a char vector.
- template <typename T>
- static void PushToVector(std::vector<char> &data, const T &obj, size_t writeSize = sizeof(T))
- {
- static_assert(!std::is_pointer<T>::value, "Won't push pointers to data vectors.");
- const char *objC = reinterpret_cast<const char *>(&obj);
- data.insert(data.end(), objC, objC + writeSize);
- }
- static void PushZStringToVector(std::vector<char> &data, const char *str)
- {
- if(str != nullptr)
- data.insert(data.end(), str, str + strlen(str));
- PushToVector(data, char(0));
- }
- OPENMPT_NAMESPACE_END
- #include "AEffectWrapper.h"
- #include "BridgeOpCodes.h"
- OPENMPT_NAMESPACE_BEGIN
- // Internal data structures
- // Event to notify other threads
- class Event
- {
- private:
- HANDLE handle = nullptr;
- public:
- Event() = default;
- ~Event() { Close(); }
- // Create a new event
- bool Create(bool manual = false, const wchar_t *name = nullptr)
- {
- Close();
- handle = CreateEventW(nullptr, manual ? TRUE : FALSE, FALSE, name);
- return handle != nullptr;
- }
- // Duplicate a local event
- bool DuplicateFrom(HANDLE source)
- {
- Close();
- return DuplicateHandle(GetCurrentProcess(), source, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS) != FALSE;
- }
- void Close()
- {
- CloseHandle(handle);
- }
- void Trigger()
- {
- SetEvent(handle);
- }
- void Reset()
- {
- ResetEvent(handle);
- }
- void operator=(const HANDLE other) { handle = other; }
- operator const HANDLE() const { return handle; }
- };
- // Two-way event
- class Signal
- {
- public:
- Event send, confirm;
- // Create new (local) signal
- bool Create()
- {
- return send.Create() && confirm.Create();
- }
- // Create signal from name (for inter-process communication)
- bool Create(const wchar_t *name, const wchar_t *addendum)
- {
- wchar_t fullName[64 + 1];
- wcscpy(fullName, name);
- wcscat(fullName, addendum);
- fullName[std::size(fullName) - 1] = L'\0';
- size_t nameLen = wcslen(fullName);
- wcscpy(fullName + nameLen, L"-s");
- bool success = send.Create(false, fullName);
- wcscpy(fullName + nameLen, L"-a");
- return success && confirm.Create(false, fullName);
- }
- // Create signal from other signal
- bool DuplicateFrom(const Signal &other)
- {
- return send.DuplicateFrom(other.send)
- && confirm.DuplicateFrom(other.confirm);
- }
- void Send()
- {
- send.Trigger();
- }
- void Confirm()
- {
- confirm.Trigger();
- }
- };
- // Memory that can be shared between processes
- class MappedMemory
- {
- protected:
- struct Header
- {
- uint32 size;
- };
- HANDLE mapFile = nullptr;
- Header *view = nullptr;
- public:
- MappedMemory() = default;
- ~MappedMemory() { Close(); }
- // Create a shared memory object.
- bool Create(const wchar_t *name, uint32 size)
- {
- Close();
- mapFile = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size + sizeof(Header), name);
- if(!mapFile)
- {
- return false;
- }
- view = static_cast<Header *>(MapViewOfFile(mapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0));
- if(!view)
- {
- return false;
- }
- view->size = size;
- return Good();
- }
- // Open an existing shared memory object.
- bool Open(const wchar_t *name)
- {
- Close();
- mapFile = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name);
- if(!mapFile)
- {
- return false;
- }
- view = static_cast<Header *>(MapViewOfFile(mapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0));
- if(!view)
- {
- return false;
- }
- return Good();
- }
- // Close this shared memory object.
- void Close()
- {
- if(mapFile)
- {
- if(view)
- {
- UnmapViewOfFile(view);
- view = nullptr;
- }
- CloseHandle(mapFile);
- mapFile = nullptr;
- }
- }
- template <typename T = void>
- T *Data() const
- {
- if(view == nullptr)
- return nullptr;
- else
- return reinterpret_cast<T *>(view + 1);
- }
- size_t Size() const
- {
- if(!view)
- return 0;
- else
- return view->size;
- }
- bool Good() const { return view != nullptr; }
- // Make a copy and detach it from the other object
- void CopyFrom(MappedMemory &other)
- {
- Close();
- mapFile = other.mapFile;
- view = other.view;
- other.mapFile = nullptr;
- other.view = nullptr;
- }
- };
- // Bridge communication data
- #pragma pack(push, 8)
- // Simple atomic value that has a guaranteed size and layout for the shared memory
- template <typename T>
- struct BridgeAtomic
- {
- public:
- BridgeAtomic() = default;
- BridgeAtomic<T> &operator=(const T value)
- {
- static_assert(sizeof(m_value) >= sizeof(T));
- MPT_ASSERT((intptr_t(&m_value) & 3) == 0);
- #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
- InterlockedExchange(&m_value, static_cast<LONG>(value));
- #else
- _InterlockedExchange(&m_value, static_cast<LONG>(value));
- #endif
- return *this;
- }
- operator T() const
- {
- #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
- return static_cast<T>(InterlockedAdd(&m_value, 0));
- #else
- return static_cast<T>(_InterlockedExchangeAdd(&m_value, 0));
- #endif
- }
- T exchange(T desired)
- {
- #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
- return static_cast<T>(InterlockedExchange(&m_value, static_cast<LONG>(desired)));
- #else
- return static_cast<T>(_InterlockedExchange(&m_value, static_cast<LONG>(desired)));
- #endif
- }
- T fetch_add(T arg)
- {
- #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
- return static_cast<T>(InterlockedExchangeAdd(&m_value, static_cast<LONG>(arg)));
- #else
- return static_cast<T>(_InterlockedExchangeAdd(&m_value, static_cast<LONG>(arg)));
- #endif
- }
- bool compare_exchange_strong(T &expected, T desired)
- {
- #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
- return InterlockedCompareExchange(&m_value, static_cast<LONG>(desired), static_cast<LONG>(expected)) == static_cast<LONG>(expected);
- #else
- return _InterlockedCompareExchange(&m_value, static_cast<LONG>(desired), static_cast<LONG>(expected)) == static_cast<LONG>(expected);
- #endif
- }
- private:
- mutable LONG m_value;
- };
- // Host-to-bridge parameter automation message
- struct AutomationQueue
- {
- struct Parameter
- {
- uint32 index;
- float value;
- };
- BridgeAtomic<int32> pendingEvents; // Number of pending automation events
- Parameter params[64]; // Automation events
- };
- // Host-to-bridge message to initiate a process call.
- struct ProcessMsg
- {
- enum ProcessType : int32
- {
- process = 0,
- processReplacing,
- processDoubleReplacing,
- };
- int32 processType;
- int32 numInputs;
- int32 numOutputs;
- int32 sampleFrames;
- // Input and output buffers follow
- };
- // General message header
- struct MsgHeader
- {
- enum BridgeMessageType : uint32
- {
- // Management messages, host to bridge
- newInstance,
- init,
- // Management messages, bridge to host
- errorMsg,
- exceptionMsg,
- // VST messages, common
- dispatch,
- // VST messages, host to bridge
- setParameter,
- getParameter,
- automate,
- };
- BridgeAtomic<bool> isUsed;
- uint32 size; // Size of complete message, including this header
- uint32 type; // See BridgeMessageType
- };
- // Host-to-bridge new instance message
- struct NewInstanceMsg : public MsgHeader
- {
- wchar_t memName[64]; // Shared memory object name;
- };
- // Host-to-bridge initialization message
- struct InitMsg : public MsgHeader
- {
- int32 result;
- int32 hostPtrSize; // Size of VstIntPtr in host
- uint32 mixBufSize; // Interal mix buffer size (for shared memory audio buffers)
- int32 pluginID; // ID to use when sending messages to host
- uint32 fullMemDump; // When crashing, create full memory dumps instead of stack dumps
- wchar_t str[_MAX_PATH]; // Plugin file to load. Out: Error message if result != 0.
- };
- // Host-to-bridge or bridge-to-host VST dispatch message
- struct DispatchMsg : public MsgHeader
- {
- int32 opcode;
- int32 index;
- int64 value;
- int64 ptr; // Usually, this will be the size of whatever ptr points to. In that case, the data itself is stored after this struct.
- float opt;
- int32 result;
- };
- // Host-to-bridge VST setParameter / getParameter message
- struct ParameterMsg : public MsgHeader
- {
- int32 index; // Parameter ID
- float value; // Parameter value (in/out)
- };
- // Bridge-to-host error message
- struct ErrorMsg : public MsgHeader
- {
- wchar_t str[64];
- };
- // Universal bridge message format
- union BridgeMessage
- {
- MsgHeader header;
- NewInstanceMsg newInstance;
- InitMsg init;
- DispatchMsg dispatch;
- ParameterMsg parameter;
- ErrorMsg error;
- uint8 dummy[2048]; // Enough space for most default structs, e.g. 2x speaker negotiation struct
- void SetType(MsgHeader::BridgeMessageType msgType, uint32 size)
- {
- header.isUsed = true;
- header.size = size;
- header.type = msgType;
- }
- void NewInstance(const wchar_t *memName)
- {
- SetType(MsgHeader::newInstance, sizeof(NewInstanceMsg));
- wcsncpy(newInstance.memName, memName, std::size(newInstance.memName) - 1);
- }
- void Init(const wchar_t *pluginPath, uint32 mixBufSize, int32 pluginID, bool fullMemDump)
- {
- SetType(MsgHeader::init, sizeof(InitMsg));
- init.result = 0;
- init.hostPtrSize = sizeof(intptr_t);
- init.mixBufSize = mixBufSize;
- init.pluginID = pluginID;
- init.fullMemDump = fullMemDump;
- wcsncpy(init.str, pluginPath, std::size(init.str) - 1);
- }
- void Dispatch(int32 opcode, int32 index, int64 value, int64 ptr, float opt, uint32 extraDataSize)
- {
- SetType(MsgHeader::dispatch, sizeof(DispatchMsg) + extraDataSize);
- dispatch.result = 0;
- dispatch.opcode = opcode;
- dispatch.index = index;
- dispatch.value = value;
- dispatch.ptr = ptr;
- dispatch.opt = opt;
- }
- void Dispatch(Vst::VstOpcodeToHost opcode, int32 index, int64 value, int64 ptr, float opt, uint32 extraDataSize)
- {
- Dispatch(static_cast<int32>(opcode), index, value, ptr, opt, extraDataSize);
- }
- void Dispatch(Vst::VstOpcodeToPlugin opcode, int32 index, int64 value, int64 ptr, float opt, uint32 extraDataSize)
- {
- Dispatch(static_cast<int32>(opcode), index, value, ptr, opt, extraDataSize);
- }
- void SetParameter(int32 index, float value)
- {
- SetType(MsgHeader::setParameter, sizeof(ParameterMsg));
- parameter.index = index;
- parameter.value = value;
- }
- void GetParameter(int32 index)
- {
- SetType(MsgHeader::getParameter, sizeof(ParameterMsg));
- parameter.index = index;
- parameter.value = 0.0f;
- }
- void Automate()
- {
- // Dummy message
- SetType(MsgHeader::automate, sizeof(MsgHeader));
- }
- void Error(const wchar_t *text)
- {
- SetType(MsgHeader::errorMsg, sizeof(ErrorMsg));
- wcsncpy(error.str, text, std::size(error.str) - 1);
- error.str[std::size(error.str) - 1] = 0;
- }
- // Copy message to target and clear delivery status
- void CopyTo(BridgeMessage &target)
- {
- std::memcpy(&target, this, std::min(static_cast<size_t>(header.size), sizeof(BridgeMessage)));
- header.isUsed = false;
- }
- };
- // This is the maximum size of dispatch data that can be sent in a message. If you want to dispatch more data, use a secondary medium for sending it along.
- inline constexpr size_t DISPATCH_DATA_SIZE = (sizeof(BridgeMessage) - sizeof(DispatchMsg));
- static_assert(DISPATCH_DATA_SIZE >= 256, "There should be room for at least 256 bytes of dispatch data!");
- // The array size should be more than enough for any realistic scenario with nested and simultaneous dispatch calls
- inline constexpr int MSG_STACK_SIZE = 16;
- using MsgStack = std::array<BridgeMessage, MSG_STACK_SIZE>;
- // Ensuring that our HWND looks the same to both 32-bit and 64-bit processes
- struct BridgeHWND
- {
- public:
- void operator=(HWND handle) { m_handle = static_cast<int32>(reinterpret_cast<intptr_t>(handle)); }
- operator HWND() const { return reinterpret_cast<HWND>(static_cast<intptr_t>(m_handle)); }
- protected:
- BridgeAtomic<int32> m_handle;
- };
- // Layout of the shared memory chunk
- struct SharedMemLayout
- {
- union
- {
- Vst::AEffect effect; // Native layout from host perspective
- AEffect32 effect32;
- AEffect64 effect64;
- };
- MsgStack ipcMessages;
- AutomationQueue automationQueue;
- Vst::VstTimeInfo timeInfo;
- BridgeHWND hostCommWindow;
- BridgeHWND bridgeCommWindow;
- int32 bridgePluginID;
- BridgeAtomic<int32> tailSize;
- BridgeAtomic<int32> audioThreadToHostMsgID;
- BridgeAtomic<int32> audioThreadToBridgeMsgID;
- };
- static_assert(sizeof(Vst::AEffect) <= sizeof(AEffect64), "Something's going very wrong here.");
- // For caching parameter information
- struct ParameterInfo
- {
- Vst::VstParameterProperties props;
- char name[64];
- char label[64];
- char display[64];
- };
- #pragma pack(pop)
- // Common variables that we will find in both the host and plugin side of the bridge (this is not shared memory)
- class BridgeCommon
- {
- public:
- BridgeCommon()
- {
- m_instanceCount++;
- }
- ~BridgeCommon()
- {
- m_instanceCount--;
- }
- protected:
- enum WindowMessage : UINT
- {
- WM_BRIDGE_KEYFIRST = WM_USER + 4000, // Must be consistent with VSTEditor.cpp!
- WM_BRIDGE_KEYLAST = WM_BRIDGE_KEYFIRST + WM_KEYLAST - WM_KEYFIRST,
- WM_BRIDGE_MESSAGE_TO_BRIDGE,
- WM_BRIDGE_MESSAGE_TO_HOST,
- WM_BRIDGE_DELETE_PLUGIN,
- WM_BRIDGE_SUCCESS = 1337,
- };
- static std::vector<BridgeCommon *> m_plugins;
- static HWND m_communicationWindow;
- static int m_instanceCount;
- static thread_local bool m_isAudioThread;
- // Signals for host <-> bridge communication
- Signal m_sigToHostAudio, m_sigToBridgeAudio;
- Signal m_sigProcessAudio;
- Event m_sigBridgeReady;
- Event m_otherProcess; // Handle of "other" process (host handle in the bridge and vice versa)
- // Shared memory segments
- MappedMemory m_queueMem; // AEffect, message, some fixed size VST structures
- MappedMemory m_processMem; // Process message + sample buffer
- MappedMemory m_getChunkMem; // effGetChunk temporary memory
- MappedMemory m_eventMem; // VstEvents memory
- // Pointer into shared memory
- SharedMemLayout /*volatile*/ *m_sharedMem = nullptr;
- // Pointer size of the "other" side of the bridge, in bytes
- int32 m_otherPtrSize = 0;
- int32 m_thisPluginID = 0;
- int32 m_otherPluginID = 0;
- static void CreateCommunicationWindow(WNDPROC windowProc)
- {
- static constexpr TCHAR windowClassName[] = _T("OpenMPTPluginBridgeCommunication");
- static bool registered = false;
- if(!registered)
- {
- registered = true;
- WNDCLASSEX wndClass;
- wndClass.cbSize = sizeof(WNDCLASSEX);
- wndClass.style = CS_HREDRAW | CS_VREDRAW;
- wndClass.lpfnWndProc = windowProc;
- wndClass.cbClsExtra = 0;
- wndClass.cbWndExtra = 0;
- wndClass.hInstance = GetModuleHandle(nullptr);
- wndClass.hIcon = nullptr;
- wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wndClass.hbrBackground = nullptr;
- wndClass.lpszMenuName = nullptr;
- wndClass.lpszClassName = windowClassName;
- wndClass.hIconSm = nullptr;
- RegisterClassEx(&wndClass);
- }
- m_communicationWindow = CreateWindow(
- windowClassName,
- _T("OpenMPT Plugin Bridge Communication"),
- WS_POPUP,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- 1,
- 1,
- HWND_MESSAGE,
- nullptr,
- GetModuleHandle(nullptr),
- nullptr);
- }
- bool CreateSignals(const wchar_t *mapName)
- {
- wchar_t readyName[64];
- wcscpy(readyName, mapName);
- wcscat(readyName, L"rdy");
- return m_sigToHostAudio.Create(mapName, L"sha")
- && m_sigToBridgeAudio.Create(mapName, L"sba")
- && m_sigProcessAudio.Create(mapName, L"prc")
- && m_sigBridgeReady.Create(false, readyName);
- }
- // Copy a message to shared memory and return relative position.
- int CopyToSharedMemory(const BridgeMessage &msg, MsgStack &stack)
- {
- MPT_ASSERT(msg.header.isUsed);
- for(int i = 0; i < MSG_STACK_SIZE; i++)
- {
- BridgeMessage &targetMsg = stack[i];
- bool expected = false;
- if(targetMsg.header.isUsed.compare_exchange_strong(expected, true))
- {
- std::memcpy(&targetMsg, &msg, std::min(sizeof(BridgeMessage), size_t(msg.header.size)));
- return i;
- }
- }
- return -1;
- }
- };
- OPENMPT_NAMESPACE_END
|