aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/pluginBridge
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/pluginBridge')
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/AEffectWrapper.h274
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.cpp1318
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.h104
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeCommon.h679
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeOpCodes.h46
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.cpp1291
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.h237
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win10.manifest15
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win7.manifest21
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win81.manifest17
-rw-r--r--Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge.rc109
11 files changed, 4111 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/AEffectWrapper.h b/Src/external_dependencies/openmpt-trunk/pluginBridge/AEffectWrapper.h
new file mode 100644
index 00000000..93c64db0
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/AEffectWrapper.h
@@ -0,0 +1,274 @@
+/*
+ * AEffectWrapper.h
+ * ----------------
+ * Purpose: Helper functions and structs for translating the VST AEffect struct between bit boundaries.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (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>
+#include "../mptrack/plugins/VstDefinitions.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+#pragma pack(push, 8)
+
+template <typename ptr_t>
+struct AEffectProto
+{
+ int32 magic;
+ ptr_t dispatcher;
+ ptr_t process;
+ ptr_t setParameter;
+ ptr_t getParameter;
+
+ int32 numPrograms;
+ uint32 numParams;
+ int32 numInputs;
+ int32 numOutputs;
+
+ int32 flags;
+
+ ptr_t resvd1;
+ ptr_t resvd2;
+
+ int32 initialDelay;
+
+ int32 realQualities;
+ int32 offQualities;
+ float ioRatio;
+
+ ptr_t object;
+ ptr_t user;
+
+ int32 uniqueID;
+ int32 version;
+
+ ptr_t processReplacing;
+ ptr_t processDoubleReplacing;
+ char future[56];
+
+ // Convert native representation to bridge representation.
+ // Don't overwrite any values managed by the bridge wrapper or host in general.
+ void FromNative(const Vst::AEffect &in)
+ {
+ magic = in.magic;
+
+ numPrograms = in.numPrograms;
+ numParams = in.numParams;
+ numInputs = in.numInputs;
+ numOutputs = in.numOutputs;
+
+ flags = in.flags;
+
+ initialDelay = in.initialDelay;
+
+ realQualities = in.realQualities;
+ offQualities = in.offQualities;
+ ioRatio = in.ioRatio;
+
+ uniqueID = in.uniqueID;
+ version = in.version;
+
+ if(in.processReplacing == nullptr)
+ flags &= ~Vst::effFlagsCanReplacing;
+ if(in.processDoubleReplacing == nullptr)
+ flags &= ~Vst::effFlagsCanDoubleReplacing;
+ }
+};
+
+using AEffect32 = AEffectProto<int32>;
+using AEffect64 = AEffectProto<int64>;
+
+#pragma pack(pop)
+
+
+// Translate a VSTEvents struct to bridge format (placed in data vector)
+static void TranslateVstEventsToBridge(std::vector<char> &outData, const Vst::VstEvents &events, int32 targetPtrSize)
+{
+ outData.reserve(outData.size() + sizeof(int32) + sizeof(Vst::VstMidiEvent) * events.numEvents);
+ // Write number of events
+ PushToVector(outData, events.numEvents);
+ // Write events
+ for(const auto event : events)
+ {
+ if(event->type == Vst::kVstSysExType)
+ {
+ // This is going to be messy since the VstMidiSysexEvent event has a different size than other events on 64-bit platforms.
+ // We are going to write the event using the target process pointer size.
+ auto sysExEvent = *static_cast<const Vst::VstMidiSysexEvent *>(event);
+ sysExEvent.byteSize = 4 * sizeof(int32) + 4 * targetPtrSize; // It's 5 int32s and 3 pointers but that means that on 64-bit platforms, the fifth int32 is padded for alignment.
+ PushToVector(outData, sysExEvent, 5 * sizeof(int32)); // Exclude the three pointers at the end for now
+ if(targetPtrSize > static_cast<int32>(sizeof(int32))) // Padding for 64-bit required?
+ outData.insert(outData.end(), targetPtrSize - sizeof(int32), 0);
+ outData.insert(outData.end(), 3 * targetPtrSize, 0); // Make space for pointer + two reserved intptr_ts
+ // Embed SysEx dump as well...
+ auto sysex = reinterpret_cast<const char *>(sysExEvent.sysexDump);
+ outData.insert(outData.end(), sysex, sysex + sysExEvent.dumpBytes);
+ } else if(event->type == Vst::kVstMidiType)
+ {
+ // randomid by Insert Piz Here sends events of type kVstMidiType, but with a claimed size of 24 bytes instead of 32.
+ Vst::VstMidiEvent midiEvent;
+ std::memcpy(&midiEvent, event, sizeof(midiEvent));
+ midiEvent.byteSize = sizeof(midiEvent);
+ PushToVector(outData, midiEvent, sizeof(midiEvent));
+ } else
+ {
+ PushToVector(outData, *event, event->byteSize);
+ }
+ }
+}
+
+
+// Translate bridge format (void *ptr) back to VSTEvents struct (placed in data vector)
+static void TranslateBridgeToVstEvents(std::vector<char> &outData, const void *inData)
+{
+ const int32 numEvents = *static_cast<const int32 *>(inData);
+
+ // First element is really a int32, but in case of 64-bit builds, the next field gets aligned anyway.
+ const size_t headerSize = sizeof(intptr_t) + sizeof(intptr_t) + sizeof(Vst::VstEvent *) * numEvents;
+ outData.reserve(headerSize + sizeof(Vst::VstMidiEvent) * numEvents);
+ outData.resize(headerSize, 0);
+ if(numEvents == 0)
+ return;
+
+ // Copy over event data (this is required for dumb SynthEdit plugins that don't copy over the event data during effProcessEvents)
+ const char *readOffset = static_cast<const char *>(inData) + sizeof(int32);
+ for(int32 i = 0; i < numEvents; i++)
+ {
+ auto *event = reinterpret_cast<const Vst::VstEvent *>(readOffset);
+ outData.insert(outData.end(), readOffset, readOffset + event->byteSize);
+ readOffset += event->byteSize;
+
+ if(event->type == Vst::kVstSysExType)
+ {
+ // Copy over sysex dump
+ auto *sysExEvent = static_cast<const Vst::VstMidiSysexEvent *>(event);
+ outData.insert(outData.end(), readOffset, readOffset + sysExEvent->dumpBytes);
+ readOffset += sysExEvent->dumpBytes;
+ }
+ }
+
+ // Write pointers
+ auto events = reinterpret_cast<Vst::VstEvents *>(outData.data());
+ events->numEvents = numEvents;
+ char *offset = outData.data() + headerSize;
+ for(int32 i = 0; i < numEvents; i++)
+ {
+ events->events[i] = reinterpret_cast<Vst::VstEvent *>(offset);
+ offset += events->events[i]->byteSize;
+ if(events->events[i]->type == Vst::kVstSysExType)
+ {
+ auto sysExEvent = static_cast<Vst::VstMidiSysexEvent *>(events->events[i]);
+ sysExEvent->sysexDump = reinterpret_cast<const std::byte *>(offset);
+ offset += sysExEvent->dumpBytes;
+ }
+ }
+}
+
+
+// Calculate the size total of the VSTEvents (without header) in bridge format
+static size_t BridgeVstEventsSize(const void *ptr)
+{
+ const int32 numEvents = *static_cast<const int32 *>(ptr);
+ size_t size = 0;
+ for(int32 i = 0; i < numEvents; i++)
+ {
+ const auto event = reinterpret_cast<const Vst::VstEvent *>(static_cast<const char *>(ptr) + sizeof(int32) + size);
+ size += event->byteSize;
+ if(event->type == Vst::kVstSysExType)
+ {
+ size += static_cast<const Vst::VstMidiSysexEvent *>(event)->dumpBytes;
+ }
+ }
+ return size;
+}
+
+
+static void TranslateVstFileSelectToBridge(std::vector<char> &outData, const Vst::VstFileSelect &fileSelect, int32 targetPtrSize)
+{
+ outData.reserve(outData.size() + sizeof(Vst::VstFileSelect) + fileSelect.numFileTypes * sizeof(Vst::VstFileType));
+ PushToVector(outData, fileSelect.command);
+ PushToVector(outData, fileSelect.type);
+ PushToVector(outData, fileSelect.macCreator);
+ PushToVector(outData, fileSelect.numFileTypes);
+ outData.insert(outData.end(), targetPtrSize, 0); // fileTypes
+ PushToVector(outData, fileSelect.title);
+ outData.insert(outData.end(), 2 * targetPtrSize, 0); // initialPath, returnPath
+ PushToVector(outData, fileSelect.sizeReturnPath);
+ if(targetPtrSize > static_cast<int32>(sizeof(int32)))
+ outData.insert(outData.end(), targetPtrSize - sizeof(int32), 0); // padding
+ outData.insert(outData.end(), targetPtrSize, 0); // returnMultiplePaths
+ PushToVector(outData, fileSelect.numReturnPaths);
+ outData.insert(outData.end(), targetPtrSize, 0); // reserved
+ PushToVector(outData, fileSelect.reserved2);
+
+ if(fileSelect.command != Vst::kVstDirectorySelect)
+ {
+ for(int32 i = 0; i < fileSelect.numFileTypes; i++)
+ {
+ PushToVector(outData, fileSelect.fileTypes[i]);
+ }
+ }
+
+ if(fileSelect.command == Vst::kVstMultipleFilesLoad)
+ {
+ outData.insert(outData.end(), fileSelect.numReturnPaths * targetPtrSize, 0);
+ for(int32 i = 0; i < fileSelect.numReturnPaths; i++)
+ {
+ PushZStringToVector(outData, fileSelect.returnMultiplePaths[i]);
+ }
+ }
+
+ PushZStringToVector(outData, fileSelect.initialPath);
+ PushZStringToVector(outData, fileSelect.returnPath);
+}
+
+
+static void TranslateBridgeToVstFileSelect(std::vector<char> &outData, const void *inData, size_t srcSize)
+{
+ outData.assign(static_cast<const char *>(inData), static_cast<const char *>(inData) + srcSize);
+
+ // Fixup pointers
+ Vst::VstFileSelect &fileSelect = *reinterpret_cast<Vst::VstFileSelect *>(outData.data());
+ auto ptrOffset = outData.data() + sizeof(Vst::VstFileSelect);
+
+ if(fileSelect.command != Vst::kVstDirectorySelect)
+ {
+ fileSelect.fileTypes = reinterpret_cast<Vst::VstFileType *>(ptrOffset);
+ ptrOffset += fileSelect.numFileTypes * sizeof(Vst::VstFileType);
+ } else
+ {
+ fileSelect.fileTypes = nullptr;
+ }
+
+ if(fileSelect.command == Vst::kVstMultipleFilesLoad)
+ {
+ fileSelect.returnMultiplePaths = reinterpret_cast<char **>(ptrOffset);
+ ptrOffset += fileSelect.numReturnPaths * sizeof(char *);
+
+ for(int32 i = 0; i < fileSelect.numReturnPaths; i++)
+ {
+ fileSelect.returnMultiplePaths[i] = ptrOffset;
+ ptrOffset += strlen(fileSelect.returnMultiplePaths[i]) + 1;
+ }
+ } else
+ {
+ fileSelect.returnMultiplePaths = nullptr;
+ }
+
+ fileSelect.initialPath = ptrOffset;
+ ptrOffset += strlen(fileSelect.initialPath) + 1;
+ fileSelect.returnPath = ptrOffset;
+ fileSelect.sizeReturnPath = static_cast<int32>(srcSize - std::distance(outData.data(), ptrOffset));
+ ptrOffset += strlen(fileSelect.returnPath) + 1;
+}
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.cpp b/Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.cpp
new file mode 100644
index 00000000..e819c97f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.cpp
@@ -0,0 +1,1318 @@
+/*
+ * Bridge.cpp
+ * ----------
+ * Purpose: VST plugin bridge (plugin side)
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+// TODO: Translate pointer-sized members in remaining structs: VstVariableIo, VstOfflineTask, VstAudioFile, VstWindow (all these are currently not supported by OpenMPT, so not urgent at all)
+
+#include "openmpt/all/BuildSettings.hpp"
+#include "../common/mptBaseMacros.h"
+#include "../common/mptBaseTypes.h"
+#include "../common/mptBaseUtils.h"
+#include <Windows.h>
+#include <ShellAPI.h>
+#include <ShlObj.h>
+#include <CommDlg.h>
+#include <tchar.h>
+#include <algorithm>
+#include <string>
+
+#if defined(MPT_BUILD_MSVC)
+#pragma comment(lib, "comdlg32.lib")
+#pragma comment(lib, "ole32.lib")
+#pragma comment(lib, "shell32.lib")
+#endif
+
+
+#if MPT_BUILD_DEBUG
+#include <intrin.h>
+#define MPT_ASSERT(x) \
+ MPT_MAYBE_CONSTANT_IF(!(x)) \
+ { \
+ if(IsDebuggerPresent()) \
+ __debugbreak(); \
+ ::MessageBoxA(nullptr, "Debug Assertion Failed:\n\n" #x, "OpenMPT Plugin Bridge", MB_ICONERROR); \
+ }
+#else
+#define MPT_ASSERT(x)
+#endif
+
+#include "../misc/WriteMemoryDump.h"
+#include "Bridge.h"
+
+
+// Crash handler for writing memory dumps
+static LONG WINAPI CrashHandler(_EXCEPTION_POINTERS *pExceptionInfo)
+{
+ WCHAR tempPath[MAX_PATH + 2];
+ DWORD result = GetTempPathW(MAX_PATH + 1, tempPath);
+ if(result > 0 && result <= MAX_PATH + 1)
+ {
+ std::wstring filename = tempPath;
+ filename += L"OpenMPT Crash Files\\";
+ CreateDirectoryW(filename.c_str(), nullptr);
+
+ tempPath[0] = 0;
+ const int ch = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, nullptr, L"'PluginBridge 'yyyy'-'MM'-'dd ", tempPath, mpt::saturate_cast<int>(std::size(tempPath)));
+ if(ch)
+ GetTimeFormatW(LOCALE_SYSTEM_DEFAULT, 0, nullptr, L"HH'.'mm'.'ss'.dmp'", tempPath + ch - 1, mpt::saturate_cast<int>(std::size(tempPath)) - ch + 1);
+ filename += tempPath;
+ OPENMPT_NAMESPACE::WriteMemoryDump(pExceptionInfo, filename.c_str(), OPENMPT_NAMESPACE::PluginBridge::m_fullMemDump);
+ }
+
+ // Let Windows handle the exception...
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+int _tmain(int argc, TCHAR *argv[])
+{
+ if(argc != 2)
+ {
+ MessageBox(nullptr, _T("This executable is part of OpenMPT. You do not need to run it by yourself."), _T("OpenMPT Plugin Bridge"), 0);
+ return -1;
+ }
+
+ ::SetUnhandledExceptionFilter(CrashHandler);
+
+ // We don't need COM, but some plugins do and don't initialize it themselves.
+ // Note 1: Which plugins? This was added in r6459 on 2016-05-31 but with no remark whether it fixed a specific plugin,
+ // but the fix doesn't seem to make a lot of sense since back then no plugin code was ever running on the main thread.
+ // Could it have been for file dialogs, which were added a while before?
+ // Note 2: M1 editor crashes if it runs on this thread and it was initialized with COINIT_MULTITHREADED
+ const bool comInitialized = SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
+
+ OPENMPT_NAMESPACE::PluginBridge::MainLoop(argv);
+
+ if(comInitialized)
+ CoUninitialize();
+
+ return 0;
+}
+
+
+int WINAPI WinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ LPSTR /*lpCmdLine*/, _In_ int /*nCmdShow*/)
+{
+ int argc = 0;
+ auto argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ return _tmain(argc, argv);
+}
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+using namespace Vst;
+
+std::vector<BridgeCommon *> BridgeCommon::m_plugins;
+HWND BridgeCommon::m_communicationWindow = nullptr;
+int BridgeCommon::m_instanceCount = 0;
+thread_local bool BridgeCommon::m_isAudioThread = false;
+
+// This is kind of a back-up pointer in case we couldn't sneak our pointer into the AEffect struct yet.
+// It always points to the last initialized PluginBridge object.
+PluginBridge *PluginBridge::m_latestInstance = nullptr;
+ATOM PluginBridge::m_editorClassAtom = 0;
+bool PluginBridge::m_fullMemDump = false;
+
+void PluginBridge::MainLoop(TCHAR *argv[])
+{
+ WNDCLASSEX editorWndClass;
+ editorWndClass.cbSize = sizeof(WNDCLASSEX);
+ editorWndClass.style = CS_HREDRAW | CS_VREDRAW;
+ editorWndClass.lpfnWndProc = WindowProc;
+ editorWndClass.cbClsExtra = 0;
+ editorWndClass.cbWndExtra = 0;
+ editorWndClass.hInstance = GetModuleHandle(nullptr);
+ editorWndClass.hIcon = nullptr;
+ editorWndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ editorWndClass.hbrBackground = nullptr;
+ editorWndClass.lpszMenuName = nullptr;
+ editorWndClass.lpszClassName = _T("OpenMPTPluginBridgeEditor");
+ editorWndClass.hIconSm = nullptr;
+ m_editorClassAtom = RegisterClassEx(&editorWndClass);
+
+ CreateCommunicationWindow(WindowProc);
+ SetTimer(m_communicationWindow, TIMER_IDLE, 20, IdleTimerProc);
+
+ uint32 parentProcessId = _ttoi(argv[1]);
+ new PluginBridge(argv[0], OpenProcess(SYNCHRONIZE, FALSE, parentProcessId));
+
+ MSG msg;
+ while(::GetMessage(&msg, nullptr, 0, 0))
+ {
+ // Let host pre-process key messages like it does for non-bridged plugins
+ if(msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
+ {
+ HWND owner = nullptr;
+ for(HWND hwnd = msg.hwnd; hwnd != nullptr; hwnd = GetParent(hwnd))
+ {
+ // Does it come from a child window? (e.g. Kirnu editor)
+ if(GetClassWord(hwnd, GCW_ATOM) == m_editorClassAtom)
+ {
+ owner = GetParent(GetParent(hwnd));
+ break;
+ }
+ // Does the message come from a top-level window? This is required e.g. for the slider pop-up windows and patch browser in Synth1.
+ if(!(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD))
+ {
+ owner = GetWindow(hwnd, GW_OWNER);
+ break;
+ }
+ }
+ // Send to top-level VST editor window in host
+ if(owner && SendMessage(owner, msg.message + WM_BRIDGE_KEYFIRST - WM_KEYFIRST, msg.wParam, msg.lParam))
+ continue;
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyWindow(m_communicationWindow);
+}
+
+
+PluginBridge::PluginBridge(const wchar_t *memName, HANDLE otherProcess)
+{
+ PluginBridge::m_latestInstance = this;
+
+ m_thisPluginID = static_cast<int32>(m_plugins.size());
+ m_plugins.push_back(this);
+
+ if(!m_queueMem.Open(memName)
+ || !CreateSignals(memName))
+ {
+ MessageBox(nullptr, _T("Could not connect to OpenMPT."), _T("OpenMPT Plugin Bridge"), 0);
+ delete this;
+ return;
+ }
+
+ m_sharedMem = m_queueMem.Data<SharedMemLayout>();
+
+ // Store parent process handle so that we can terminate the bridge process when OpenMPT closes (e.g. through a crash).
+ m_otherProcess.DuplicateFrom(otherProcess);
+
+ m_sigThreadExit.Create(true);
+ DWORD dummy = 0; // For Win9x
+ m_audioThread = CreateThread(NULL, 0, &PluginBridge::AudioThread, this, 0, &dummy);
+
+ m_sharedMem->bridgeCommWindow = m_communicationWindow;
+ m_sharedMem->bridgePluginID = m_thisPluginID;
+ m_sigBridgeReady.Trigger();
+}
+
+
+PluginBridge::~PluginBridge()
+{
+ SignalObjectAndWait(m_sigThreadExit, m_audioThread, INFINITE, FALSE);
+ CloseHandle(m_audioThread);
+ BridgeMessage dispatchMsg;
+ dispatchMsg.Dispatch(effClose, 0, 0, 0, 0.0f, 0);
+ DispatchToPlugin(dispatchMsg.dispatch);
+ m_plugins[m_thisPluginID] = nullptr;
+ if(m_instanceCount == 1)
+ PostQuitMessage(0);
+}
+
+
+void PluginBridge::RequestDelete()
+{
+ PostMessage(m_communicationWindow, WM_BRIDGE_DELETE_PLUGIN, m_thisPluginID, 0);
+}
+
+
+// Send an arbitrary message to the host.
+// Returns true if the message was processed by the host.
+bool PluginBridge::SendToHost(BridgeMessage &sendMsg)
+{
+ auto &messages = m_sharedMem->ipcMessages;
+ const auto msgID = CopyToSharedMemory(sendMsg, messages);
+ if(msgID < 0)
+ return false;
+ BridgeMessage &sharedMsg = messages[msgID];
+
+ if(!m_isAudioThread)
+ {
+ if(SendMessage(m_sharedMem->hostCommWindow, WM_BRIDGE_MESSAGE_TO_HOST, m_otherPluginID, msgID) == WM_BRIDGE_SUCCESS)
+ {
+ sharedMsg.CopyTo(sendMsg);
+ return true;
+ }
+ return false;
+ }
+
+ // Audio thread: Use signals instead of window messages
+ m_sharedMem->audioThreadToHostMsgID = msgID;
+ m_sigToHostAudio.Send();
+
+ // Wait until we get the result from the host.
+ DWORD result;
+ const HANDLE objects[] = {m_sigToHostAudio.confirm, m_sigToBridgeAudio.send, m_otherProcess};
+ do
+ {
+ result = WaitForMultipleObjects(mpt::saturate_cast<DWORD>(std::size(objects)), objects, FALSE, INFINITE);
+ if(result == WAIT_OBJECT_0)
+ {
+ // Message got answered
+ sharedMsg.CopyTo(sendMsg);
+ break;
+ } else if(result == WAIT_OBJECT_0 + 1)
+ {
+ ParseNextMessage(m_sharedMem->audioThreadToBridgeMsgID);
+ m_sigToBridgeAudio.Confirm();
+ }
+ } while(result != WAIT_OBJECT_0 + 2 && result != WAIT_FAILED);
+
+ return (result == WAIT_OBJECT_0);
+}
+
+
+// Copy AEffect to shared memory.
+void PluginBridge::UpdateEffectStruct()
+{
+ if(m_nativeEffect == nullptr)
+ return;
+ else if(m_otherPtrSize == 4)
+ m_sharedMem->effect32.FromNative(*m_nativeEffect);
+ else if(m_otherPtrSize == 8)
+ m_sharedMem->effect64.FromNative(*m_nativeEffect);
+ else
+ MPT_ASSERT(false);
+}
+
+
+// Create the memory-mapped file containing the processing message and audio buffers
+void PluginBridge::CreateProcessingFile(std::vector<char> &dispatchData)
+{
+ static uint32 plugId = 0;
+ wchar_t mapName[64];
+ swprintf(mapName, std::size(mapName), L"Local\\openmpt-%u-%u", GetCurrentProcessId(), plugId++);
+
+ PushToVector(dispatchData, mapName[0], sizeof(mapName));
+
+ if(!m_processMem.Create(mapName, sizeof(ProcessMsg) + m_mixBufSize * (m_nativeEffect->numInputs + m_nativeEffect->numOutputs) * sizeof(double)))
+ {
+ SendErrorMessage(L"Could not initialize plugin bridge audio memory.");
+ return;
+ }
+}
+
+
+// Receive a message from the host and translate it.
+void PluginBridge::ParseNextMessage(int msgID)
+{
+ auto &msg = m_sharedMem->ipcMessages[msgID];
+ switch(msg.header.type)
+ {
+ case MsgHeader::newInstance:
+ NewInstance(msg.newInstance);
+ break;
+ case MsgHeader::init:
+ InitBridge(msg.init);
+ break;
+ case MsgHeader::dispatch:
+ DispatchToPlugin(msg.dispatch);
+ break;
+ case MsgHeader::setParameter:
+ SetParameter(msg.parameter);
+ break;
+ case MsgHeader::getParameter:
+ GetParameter(msg.parameter);
+ break;
+ case MsgHeader::automate:
+ AutomateParameters();
+ break;
+ }
+}
+
+
+// Create a new bridge instance within this one (creates a new thread).
+void PluginBridge::NewInstance(NewInstanceMsg &msg)
+{
+ msg.memName[mpt::array_size<decltype(msg.memName)>::size - 1] = 0;
+ new PluginBridge(msg.memName, m_otherProcess);
+}
+
+
+// Load the plugin.
+void PluginBridge::InitBridge(InitMsg &msg)
+{
+ m_otherPtrSize = msg.hostPtrSize;
+ m_mixBufSize = msg.mixBufSize;
+ m_otherPluginID = msg.pluginID;
+ m_fullMemDump = msg.fullMemDump != 0;
+ msg.result = 0;
+ msg.str[mpt::array_size<decltype(msg.str)>::size - 1] = 0;
+
+#ifdef _CONSOLE
+ SetConsoleTitleW(msg->str);
+#endif
+
+ m_nativeEffect = nullptr;
+ __try
+ {
+ m_library = LoadLibraryW(msg.str);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ m_library = nullptr;
+ }
+
+ if(m_library == nullptr)
+ {
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), msg.str, mpt::saturate_cast<DWORD>(std::size(msg.str)), nullptr);
+ RequestDelete();
+ return;
+ }
+
+ auto mainProc = (Vst::MainProc)GetProcAddress(m_library, "VSTPluginMain");
+ if(mainProc == nullptr)
+ {
+ mainProc = (Vst::MainProc)GetProcAddress(m_library, "main");
+ }
+
+ if(mainProc != nullptr)
+ {
+ __try
+ {
+ m_nativeEffect = mainProc(MasterCallback);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ m_nativeEffect = nullptr;
+ }
+ }
+
+ if(m_nativeEffect == nullptr || m_nativeEffect->dispatcher == nullptr || m_nativeEffect->magic != kEffectMagic)
+ {
+ FreeLibrary(m_library);
+ m_library = nullptr;
+
+ wcscpy(msg.str, L"File is not a valid plugin");
+ RequestDelete();
+ return;
+ }
+
+ m_nativeEffect->reservedForHost1 = this;
+
+ msg.result = 1;
+
+ UpdateEffectStruct();
+
+ // Init process buffer
+ DispatchToHost(audioMasterVendorSpecific, kVendorOpenMPT, kUpdateProcessingBuffer, nullptr, 0.0f);
+}
+
+
+void PluginBridge::SendErrorMessage(const wchar_t *str)
+{
+ BridgeMessage msg;
+ msg.Error(str);
+ SendToHost(msg);
+}
+
+
+// Wrapper for VST dispatch call with structured exception handling.
+static intptr_t DispatchSEH(AEffect *effect, VstOpcodeToPlugin opCode, int32 index, intptr_t value, void *ptr, float opt, bool &exception)
+{
+ __try
+ {
+ if(effect->dispatcher != nullptr)
+ {
+ return effect->dispatcher(effect, opCode, index, value, ptr, opt);
+ }
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ exception = true;
+ }
+ return 0;
+}
+
+
+// Host-to-plugin opcode dispatcher
+void PluginBridge::DispatchToPlugin(DispatchMsg &msg)
+{
+ if(m_nativeEffect == nullptr)
+ {
+ return;
+ }
+
+ // Various dispatch data - depending on the opcode, one of those might be used.
+ std::vector<char> extraData;
+ size_t extraDataSize = 0;
+
+ MappedMemory auxMem;
+
+ // Content of ptr is usually stored right after the message header, ptr field indicates size.
+ void *ptr = (msg.ptr != 0) ? (&msg + 1) : nullptr;
+ if(msg.size > sizeof(BridgeMessage))
+ {
+ if(!auxMem.Open(static_cast<const wchar_t *>(ptr)))
+ {
+ return;
+ }
+ ptr = auxMem.Data();
+ }
+ void *origPtr = ptr;
+
+ switch(msg.opcode)
+ {
+ case effGetProgramName:
+ case effGetParamLabel:
+ case effGetParamDisplay:
+ case effGetParamName:
+ case effString2Parameter:
+ case effGetProgramNameIndexed:
+ case effGetEffectName:
+ case effGetErrorText:
+ case effGetVendorString:
+ case effGetProductString:
+ case effShellGetNextPlugin:
+ // Name in [ptr]
+ extraDataSize = 256;
+ break;
+
+ case effMainsChanged:
+ // [value]: 0 means "turn off", 1 means "turn on"
+ ::SetThreadPriority(m_audioThread, msg.value ? THREAD_PRIORITY_ABOVE_NORMAL : THREAD_PRIORITY_NORMAL);
+ m_sharedMem->tailSize = static_cast<int32>(Dispatch(effGetTailSize, 0, 0, nullptr, 0.0f));
+ break;
+
+ case effEditGetRect:
+ // ERect** in [ptr]
+ extraDataSize = sizeof(void *);
+ break;
+
+ case effEditOpen:
+ // HWND in [ptr] - Note: Window handles are interoperable between 32-bit and 64-bit applications in Windows (http://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx)
+ {
+ TCHAR str[_MAX_PATH];
+ GetModuleFileName(m_library, str, mpt::saturate_cast<DWORD>(std::size(str)));
+
+ const auto parentWindow = reinterpret_cast<HWND>(msg.ptr);
+ ptr = m_window = CreateWindow(
+ MAKEINTATOM(m_editorClassAtom),
+ str,
+ WS_CHILD | WS_VISIBLE,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ 1, 1,
+ parentWindow,
+ nullptr,
+ GetModuleHandle(nullptr),
+ nullptr);
+ SetWindowLongPtr(m_window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
+ }
+ break;
+
+ case effGetChunk:
+ // void** in [ptr] for chunk data address
+ extraDataSize = sizeof(void *);
+ break;
+
+ case effProcessEvents:
+ // VstEvents* in [ptr]
+ TranslateBridgeToVstEvents(m_eventCache, ptr);
+ ptr = m_eventCache.data();
+ break;
+
+ case effOfflineNotify:
+ // VstAudioFile* in [ptr]
+ extraData.resize(sizeof(VstAudioFile *) * static_cast<size_t>(msg.value));
+ ptr = extraData.data();
+ for(int64 i = 0; i < msg.value; i++)
+ {
+ // TODO create pointers
+ }
+ break;
+
+ case effOfflinePrepare:
+ case effOfflineRun:
+ // VstOfflineTask* in [ptr]
+ extraData.resize(sizeof(VstOfflineTask *) * static_cast<size_t>(msg.value));
+ ptr = extraData.data();
+ for(int64 i = 0; i < msg.value; i++)
+ {
+ // TODO create pointers
+ }
+ break;
+
+ case effSetSpeakerArrangement:
+ case effGetSpeakerArrangement:
+ // VstSpeakerArrangement* in [value] and [ptr]
+ msg.value = reinterpret_cast<int64>(ptr) + sizeof(VstSpeakerArrangement);
+ break;
+
+ case effVendorSpecific:
+ // Let's implement some custom opcodes!
+ if(msg.index == kVendorOpenMPT)
+ {
+ msg.result = 1;
+ switch(msg.value)
+ {
+ case kUpdateEffectStruct:
+ UpdateEffectStruct();
+ break;
+ case kUpdateEventMemName:
+ if(ptr)
+ m_eventMem.Open(static_cast<const wchar_t *>(ptr));
+ break;
+ case kCacheProgramNames:
+ if(ptr)
+ {
+ int32 progMin = static_cast<const int32 *>(ptr)[0];
+ int32 progMax = static_cast<const int32 *>(ptr)[1];
+ char *name = static_cast<char *>(ptr);
+ for(int32 i = progMin; i < progMax; i++)
+ {
+ strcpy(name, "");
+ if(m_nativeEffect->numPrograms <= 0 || Dispatch(effGetProgramNameIndexed, i, -1, name, 0) != 1)
+ {
+ // Fallback: Try to get current program name.
+ strcpy(name, "");
+ int32 curProg = static_cast<int32>(Dispatch(effGetProgram, 0, 0, nullptr, 0.0f));
+ if(i != curProg)
+ {
+ Dispatch(effSetProgram, 0, i, nullptr, 0.0f);
+ }
+ Dispatch(effGetProgramName, 0, 0, name, 0);
+ if(i != curProg)
+ {
+ Dispatch(effSetProgram, 0, curProg, nullptr, 0.0f);
+ }
+ }
+ name[kCachedProgramNameLength - 1] = '\0';
+ name += kCachedProgramNameLength;
+ }
+ }
+ break;
+ case kCacheParameterInfo:
+ if(ptr)
+ {
+ int32 paramMin = static_cast<const int32 *>(ptr)[0];
+ int32 paramMax = static_cast<const int32 *>(ptr)[1];
+ ParameterInfo *param = static_cast<ParameterInfo *>(ptr);
+ for(int32 i = paramMin; i < paramMax; i++, param++)
+ {
+ strcpy(param->name, "");
+ strcpy(param->label, "");
+ strcpy(param->display, "");
+ Dispatch(effGetParamName, i, 0, param->name, 0.0f);
+ Dispatch(effGetParamLabel, i, 0, param->label, 0.0f);
+ Dispatch(effGetParamDisplay, i, 0, param->display, 0.0f);
+ param->name[mpt::array_size<decltype(param->label)>::size - 1] = '\0';
+ param->label[mpt::array_size<decltype(param->label)>::size - 1] = '\0';
+ param->display[mpt::array_size<decltype(param->display)>::size - 1] = '\0';
+
+ if(Dispatch(effGetParameterProperties, i, 0, &param->props, 0.0f) != 1)
+ {
+ memset(&param->props, 0, sizeof(param->props));
+ strncpy(param->props.label, param->name, std::size(param->props.label));
+ }
+ }
+ }
+ break;
+ case kBeginGetProgram:
+ if(ptr)
+ {
+ int32 numParams = static_cast<int32>((msg.size - sizeof(DispatchMsg)) / sizeof(float));
+ float *params = static_cast<float *>(ptr);
+ for(int32 i = 0; i < numParams; i++)
+ {
+ params[i] = m_nativeEffect->getParameter(m_nativeEffect, i);
+ }
+ }
+ break;
+ default:
+ msg.result = 0;
+ }
+ return;
+ }
+ break;
+ }
+
+ if(extraDataSize != 0)
+ {
+ extraData.resize(extraDataSize, 0);
+ ptr = extraData.data();
+ }
+
+ //std::cout << "about to dispatch " << msg.opcode << " to effect...";
+ //std::flush(std::cout);
+ bool exception = false;
+ msg.result = static_cast<int32>(DispatchSEH(m_nativeEffect, static_cast<VstOpcodeToPlugin>(msg.opcode), msg.index, static_cast<intptr_t>(msg.value), ptr, msg.opt, exception));
+ if(exception && msg.opcode != effClose)
+ {
+ msg.type = MsgHeader::exceptionMsg;
+ return;
+ }
+ //std::cout << "done" << std::endl;
+
+ // Post-fix some opcodes
+ switch(msg.opcode)
+ {
+ case effClose:
+ m_nativeEffect = nullptr;
+ FreeLibrary(m_library);
+ m_library = nullptr;
+ RequestDelete();
+ return;
+
+ case effGetProgramName:
+ case effGetParamLabel:
+ case effGetParamDisplay:
+ case effGetParamName:
+ case effString2Parameter:
+ case effGetProgramNameIndexed:
+ case effGetEffectName:
+ case effGetErrorText:
+ case effGetVendorString:
+ case effGetProductString:
+ case effShellGetNextPlugin:
+ // Name in [ptr]
+ {
+ extraData.back() = 0;
+ char *dst = static_cast<char *>(origPtr);
+ size_t length = static_cast<size_t>(msg.ptr - 1);
+ strncpy(dst, extraData.data(), length);
+ dst[length] = 0;
+ break;
+ }
+
+ case effEditGetRect:
+ // ERect** in [ptr]
+ {
+ ERect *rectPtr = *reinterpret_cast<ERect **>(extraData.data());
+ if(rectPtr != nullptr && origPtr != nullptr)
+ {
+ MPT_ASSERT(static_cast<size_t>(msg.ptr) >= sizeof(ERect));
+ std::memcpy(origPtr, rectPtr, std::min(sizeof(ERect), static_cast<size_t>(msg.ptr)));
+ m_windowWidth = rectPtr->right - rectPtr->left;
+ m_windowHeight = rectPtr->bottom - rectPtr->top;
+
+ // For plugins that don't know their size until after effEditOpen is done.
+ if(m_window)
+ {
+ SetWindowPos(m_window, nullptr, 0, 0, m_windowWidth, m_windowHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ }
+ break;
+ }
+
+ case effEditClose:
+ DestroyWindow(m_window);
+ m_window = nullptr;
+ break;
+
+ case effGetChunk:
+ // void** in [ptr] for chunk data address
+ if(m_getChunkMem.Create(static_cast<const wchar_t *>(origPtr), msg.result))
+ {
+ std::memcpy(m_getChunkMem.Data(), *reinterpret_cast<void **>(extraData.data()), msg.result);
+ }
+ break;
+ }
+
+ UpdateEffectStruct(); // Regularly update the struct
+}
+
+
+intptr_t PluginBridge::Dispatch(VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt)
+{
+ __try
+ {
+ return m_nativeEffect->dispatcher(m_nativeEffect, opcode, index, value, ptr, opt);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SendErrorMessage(L"Exception in dispatch()!");
+ }
+ return 0;
+}
+
+
+// Set a plugin parameter.
+void PluginBridge::SetParameter(ParameterMsg &msg)
+{
+ __try
+ {
+ m_nativeEffect->setParameter(m_nativeEffect, msg.index, msg.value);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ msg.type = MsgHeader::exceptionMsg;
+ }
+}
+
+
+// Get a plugin parameter.
+void PluginBridge::GetParameter(ParameterMsg &msg)
+{
+ __try
+ {
+ msg.value = m_nativeEffect->getParameter(m_nativeEffect, msg.index);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ msg.type = MsgHeader::exceptionMsg;
+ }
+}
+
+
+// Execute received parameter automation messages
+void PluginBridge::AutomateParameters()
+{
+ __try
+ {
+ const AutomationQueue::Parameter *param = m_sharedMem->automationQueue.params;
+ const AutomationQueue::Parameter *paramEnd = param + std::min(m_sharedMem->automationQueue.pendingEvents.exchange(0), static_cast<int32>(std::size(m_sharedMem->automationQueue.params)));
+ while(param != paramEnd)
+ {
+ m_nativeEffect->setParameter(m_nativeEffect, param->index, param->value);
+ param++;
+ }
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SendErrorMessage(L"Exception in setParameter()!");
+ }
+}
+
+
+// Audio rendering thread
+DWORD WINAPI PluginBridge::AudioThread(LPVOID param)
+{
+ static_cast<PluginBridge*>(param)->AudioThread();
+ return 0;
+}
+void PluginBridge::AudioThread()
+{
+ m_isAudioThread = true;
+
+ const HANDLE objects[] = {m_sigProcessAudio.send, m_sigToBridgeAudio.send, m_sigThreadExit, m_otherProcess};
+ DWORD result = 0;
+ do
+ {
+ result = WaitForMultipleObjects(mpt::saturate_cast<DWORD>(std::size(objects)), objects, FALSE, INFINITE);
+ if(result == WAIT_OBJECT_0)
+ {
+ ProcessMsg *msg = m_processMem.Data<ProcessMsg>();
+ AutomateParameters();
+
+ m_sharedMem->tailSize = static_cast<int32>(Dispatch(effGetTailSize, 0, 0, nullptr, 0.0f));
+ m_isProcessing = true;
+
+ // Prepare VstEvents.
+ if(auto *events = m_eventMem.Data<int32>(); events != nullptr && *events != 0)
+ {
+ TranslateBridgeToVstEvents(m_eventCache, events);
+ *events = 0;
+ Dispatch(effProcessEvents, 0, 0, m_eventCache.data(), 0.0f);
+ }
+
+ switch(msg->processType)
+ {
+ case ProcessMsg::process:
+ Process();
+ break;
+ case ProcessMsg::processReplacing:
+ ProcessReplacing();
+ break;
+ case ProcessMsg::processDoubleReplacing:
+ ProcessDoubleReplacing();
+ break;
+ }
+
+ m_isProcessing = false;
+ m_sigProcessAudio.Confirm();
+ } else if(result == WAIT_OBJECT_0 + 1)
+ {
+ ParseNextMessage(m_sharedMem->audioThreadToBridgeMsgID);
+ m_sigToBridgeAudio.Confirm();
+ } else if(result == WAIT_OBJECT_0 + 2)
+ {
+ // Main thread asked for termination
+ break;
+ } else if(result == WAIT_OBJECT_0 + 3)
+ {
+ // Host process died
+ RequestDelete();
+ break;
+ }
+ } while(result != WAIT_FAILED);
+}
+
+
+// Process audio.
+void PluginBridge::Process()
+{
+ if(m_nativeEffect->process)
+ {
+ float **inPointers, **outPointers;
+ int32 sampleFrames = BuildProcessPointers(inPointers, outPointers);
+ __try
+ {
+ m_nativeEffect->process(m_nativeEffect, inPointers, outPointers, sampleFrames);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SendErrorMessage(L"Exception in process()!");
+ }
+ }
+}
+
+
+// Process audio.
+void PluginBridge::ProcessReplacing()
+{
+ if(m_nativeEffect->processReplacing)
+ {
+ float **inPointers, **outPointers;
+ int32 sampleFrames = BuildProcessPointers(inPointers, outPointers);
+ __try
+ {
+ m_nativeEffect->processReplacing(m_nativeEffect, inPointers, outPointers, sampleFrames);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SendErrorMessage(L"Exception in processReplacing()!");
+ }
+ }
+}
+
+
+// Process audio.
+void PluginBridge::ProcessDoubleReplacing()
+{
+ if(m_nativeEffect->processDoubleReplacing)
+ {
+ double **inPointers, **outPointers;
+ int32 sampleFrames = BuildProcessPointers(inPointers, outPointers);
+ __try
+ {
+ m_nativeEffect->processDoubleReplacing(m_nativeEffect, inPointers, outPointers, sampleFrames);
+ } __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SendErrorMessage(L"Exception in processDoubleReplacing()!");
+ }
+ }
+}
+
+
+// Helper function to build the pointer arrays required by the VST process functions.
+template <typename buf_t>
+int32 PluginBridge::BuildProcessPointers(buf_t **(&inPointers), buf_t **(&outPointers))
+{
+ MPT_ASSERT(m_processMem.Good());
+ ProcessMsg &msg = *m_processMem.Data<ProcessMsg>();
+
+ const size_t numPtrs = msg.numInputs + msg.numOutputs;
+ m_sampleBuffers.resize(numPtrs, 0);
+
+ if(numPtrs)
+ {
+ buf_t *offset = reinterpret_cast<buf_t *>(&msg + 1);
+ for(size_t i = 0; i < numPtrs; i++)
+ {
+ m_sampleBuffers[i] = offset;
+ offset += msg.sampleFrames;
+ }
+ inPointers = reinterpret_cast<buf_t **>(m_sampleBuffers.data());
+ outPointers = inPointers + msg.numInputs;
+ }
+
+ return msg.sampleFrames;
+}
+
+
+// Send a message to the host.
+intptr_t PluginBridge::DispatchToHost(VstOpcodeToHost opcode, int32 index, intptr_t value, void *ptr, float opt)
+{
+ std::vector<char> dispatchData(sizeof(DispatchMsg), 0);
+ int64 ptrOut = 0;
+ char *ptrC = static_cast<char *>(ptr);
+
+ switch(opcode)
+ {
+ case audioMasterAutomate:
+ case audioMasterVersion:
+ case audioMasterCurrentId:
+ case audioMasterIdle:
+ case audioMasterPinConnected:
+ break;
+
+ case audioMasterWantMidi:
+ return 1;
+
+ case audioMasterGetTime:
+ // VstTimeInfo* in [return value]
+ if(m_isProcessing)
+ {
+ // During processing, read the cached time info. It won't change during the call
+ // and we can save some valuable inter-process calls that way.
+ return ToIntPtr<VstTimeInfo>(&m_sharedMem->timeInfo);
+ }
+ break;
+
+ case audioMasterProcessEvents:
+ // VstEvents* in [ptr]
+ if(ptr == nullptr)
+ return 0;
+ TranslateVstEventsToBridge(dispatchData, *static_cast<VstEvents *>(ptr), m_otherPtrSize);
+ ptrOut = dispatchData.size() - sizeof(DispatchMsg);
+ // If we are currently processing, try to return the events as part of the process call
+ if(m_isAudioThread && m_isProcessing && m_eventMem.Good())
+ {
+ auto *memBytes = m_eventMem.Data<char>();
+ auto *eventBytes = m_eventMem.Data<char>() + sizeof(int32);
+ int32 &memNumEvents = *m_eventMem.Data<int32>();
+ const auto memEventsSize = BridgeVstEventsSize(memBytes);
+ if(m_eventMem.Size() >= static_cast<size_t>(ptrOut) + memEventsSize)
+ {
+ // Enough shared memory for possibly pre-existing and new events; add new events at the end
+ memNumEvents += static_cast<VstEvents *>(ptr)->numEvents;
+ std::memcpy(eventBytes + memEventsSize, dispatchData.data() + sizeof(DispatchMsg) + sizeof(int32), static_cast<size_t>(ptrOut) - sizeof(int32));
+ return 1;
+ } else if(memNumEvents)
+ {
+ // Not enough memory; merge what we have and what we want to add so that it arrives in the correct order
+ dispatchData.insert(dispatchData.begin() + sizeof(DispatchMsg) + sizeof(int32), eventBytes, eventBytes + memEventsSize);
+ *reinterpret_cast<int32 *>(dispatchData.data() + sizeof(DispatchMsg)) += memNumEvents;
+ memNumEvents = 0;
+ ptrOut += memEventsSize;
+ }
+ }
+ break;
+
+ case audioMasterSetTime:
+ case audioMasterTempoAt:
+ case audioMasterGetNumAutomatableParameters:
+ case audioMasterGetParameterQuantization:
+ break;
+
+ case audioMasterVendorSpecific:
+ if(index != kVendorOpenMPT || value != kUpdateProcessingBuffer)
+ {
+ if(ptr != 0)
+ {
+ // Cannot translate this.
+ return 0;
+ }
+ break;
+ }
+ [[fallthrough]];
+ case audioMasterIOChanged:
+ // We need to be sure that the new values are known to the master.
+ if(m_nativeEffect != nullptr)
+ {
+ UpdateEffectStruct();
+ CreateProcessingFile(dispatchData);
+ ptrOut = dispatchData.size() - sizeof(DispatchMsg);
+ }
+ break;
+
+ case audioMasterNeedIdle:
+ m_needIdle = true;
+ return 1;
+
+ case audioMasterSizeWindow:
+ if(m_window)
+ {
+ SetWindowPos(m_window, nullptr, 0, 0, index, static_cast<int>(value), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ break;
+
+ case audioMasterGetSampleRate:
+ case audioMasterGetBlockSize:
+ case audioMasterGetInputLatency:
+ case audioMasterGetOutputLatency:
+ break;
+
+ case audioMasterGetPreviousPlug:
+ case audioMasterGetNextPlug:
+ // Don't even bother, this would explode :)
+ return 0;
+
+ case audioMasterWillReplaceOrAccumulate:
+ case audioMasterGetCurrentProcessLevel:
+ case audioMasterGetAutomationState:
+ break;
+
+ case audioMasterOfflineStart:
+ case audioMasterOfflineRead:
+ case audioMasterOfflineWrite:
+ case audioMasterOfflineGetCurrentPass:
+ case audioMasterOfflineGetCurrentMetaPass:
+ // Currently not supported in OpenMPT
+ return 0;
+
+ case audioMasterSetOutputSampleRate:
+ break;
+
+ case audioMasterGetOutputSpeakerArrangement:
+ case audioMasterGetInputSpeakerArrangement:
+ // VstSpeakerArrangement* in [return value]
+ ptrOut = sizeof(VstSpeakerArrangement);
+ break;
+
+ case audioMasterGetVendorString:
+ case audioMasterGetProductString:
+ // Name in [ptr]
+ ptrOut = 256;
+ break;
+
+ case audioMasterGetVendorVersion:
+ case audioMasterSetIcon:
+ break;
+
+ case audioMasterCanDo:
+ // Name in [ptr]
+ ptrOut = strlen(ptrC) + 1;
+ dispatchData.insert(dispatchData.end(), ptrC, ptrC + ptrOut);
+ break;
+
+ case audioMasterGetLanguage:
+ case audioMasterOpenWindow:
+ case audioMasterCloseWindow:
+ break;
+
+ case audioMasterGetDirectory:
+ // Name in [return value]
+ ptrOut = 256;
+ break;
+
+ case audioMasterUpdateDisplay:
+ case audioMasterBeginEdit:
+ case audioMasterEndEdit:
+ break;
+
+ case audioMasterOpenFileSelector:
+ // VstFileSelect* in [ptr]
+ if(ptr != nullptr)
+ {
+ auto &fileSel = *static_cast<VstFileSelect *>(ptr);
+ fileSel.returnMultiplePaths = nullptr;
+ fileSel.numReturnPaths = 0;
+ TranslateVstFileSelectToBridge(dispatchData, fileSel, m_otherPtrSize);
+ ptrOut = dispatchData.size() - sizeof(DispatchMsg) + 65536; // enough space for return paths
+ }
+ break;
+
+ case audioMasterCloseFileSelector:
+ // VstFileSelect* in [ptr]
+ if(auto *fileSel = static_cast<VstFileSelect *>(ptr); fileSel != nullptr && fileSel->reserved == 1)
+ {
+ fileSel->returnPath = nullptr;
+ fileSel->returnMultiplePaths = nullptr;
+ }
+ m_fileSelectCache.clear();
+ m_fileSelectCache.shrink_to_fit();
+ return 1;
+
+ case audioMasterEditFile:
+ break;
+
+ case audioMasterGetChunkFile:
+ // Name in [ptr]
+ ptrOut = 256;
+ dispatchData.insert(dispatchData.end(), ptrC, ptrC + ptrOut);
+ break;
+
+ default:
+#ifdef MPT_BUILD_DEBUG
+ if(ptr != nullptr)
+ __debugbreak();
+#endif
+ break;
+ }
+
+ if(ptrOut != 0)
+ {
+ // In case we only reserve space and don't copy stuff over...
+ dispatchData.resize(sizeof(DispatchMsg) + static_cast<size_t>(ptrOut), 0);
+ }
+
+ uint32 extraSize = static_cast<uint32>(dispatchData.size() - sizeof(DispatchMsg));
+
+ // Create message header
+ BridgeMessage *msg = reinterpret_cast<BridgeMessage *>(dispatchData.data());
+ msg->Dispatch(opcode, index, value, ptrOut, opt, extraSize);
+
+ const bool useAuxMem = dispatchData.size() > sizeof(BridgeMessage);
+ MappedMemory auxMem;
+ if(useAuxMem)
+ {
+ // Extra data doesn't fit in message - use secondary memory
+ wchar_t auxMemName[64];
+ static_assert(sizeof(DispatchMsg) + sizeof(auxMemName) <= sizeof(BridgeMessage), "Check message sizes, this will crash!");
+ swprintf(auxMemName, std::size(auxMemName), L"Local\\openmpt-%u-auxmem-%u", GetCurrentProcessId(), GetCurrentThreadId());
+ if(auxMem.Create(auxMemName, extraSize))
+ {
+ // Move message data to shared memory and then move shared memory name to message data
+ std::memcpy(auxMem.Data(), &dispatchData[sizeof(DispatchMsg)], extraSize);
+ std::memcpy(&dispatchData[sizeof(DispatchMsg)], auxMemName, sizeof(auxMemName));
+ } else
+ {
+ return 0;
+ }
+ }
+
+ //std::cout << "about to dispatch " << opcode << " to host...";
+ //std::flush(std::cout);
+ if(!SendToHost(*msg))
+ {
+ return 0;
+ }
+ //std::cout << "done." << std::endl;
+ const DispatchMsg *resultMsg = &msg->dispatch;
+
+ const char *extraData = useAuxMem ? auxMem.Data<const char>() : reinterpret_cast<const char *>(resultMsg + 1);
+ // Post-fix some opcodes
+ switch(opcode)
+ {
+ case audioMasterGetTime:
+ // VstTimeInfo* in [return value]
+ return ToIntPtr<VstTimeInfo>(&m_sharedMem->timeInfo);
+
+ case audioMasterGetOutputSpeakerArrangement:
+ case audioMasterGetInputSpeakerArrangement:
+ // VstSpeakerArrangement* in [return value]
+ std::memcpy(&m_host2PlugMem.speakerArrangement, extraData, sizeof(VstSpeakerArrangement));
+ return ToIntPtr<VstSpeakerArrangement>(&m_host2PlugMem.speakerArrangement);
+
+ case audioMasterGetVendorString:
+ case audioMasterGetProductString:
+ // Name in [ptr]
+ strcpy(ptrC, extraData);
+ break;
+
+ case audioMasterGetDirectory:
+ // Name in [return value]
+ strncpy(m_host2PlugMem.name, extraData, std::size(m_host2PlugMem.name) - 1);
+ m_host2PlugMem.name[std::size(m_host2PlugMem.name) - 1] = 0;
+ return ToIntPtr<char>(m_host2PlugMem.name);
+
+ case audioMasterOpenFileSelector:
+ if(resultMsg->result != 0 && ptr != nullptr)
+ {
+ TranslateBridgeToVstFileSelect(m_fileSelectCache, extraData, static_cast<size_t>(ptrOut));
+ auto &fileSel = *static_cast<VstFileSelect *>(ptr);
+ const auto &fileSelResult = *reinterpret_cast<const VstFileSelect *>(m_fileSelectCache.data());
+
+ if((fileSel.command == kVstFileLoad || fileSel.command == kVstFileSave))
+ {
+ if(FourCC("VOPM") == m_nativeEffect->uniqueID)
+ {
+ fileSel.sizeReturnPath = _MAX_PATH;
+ }
+ } else if(fileSel.command == kVstDirectorySelect)
+ {
+ if(FourCC("VSTr") == m_nativeEffect->uniqueID && fileSel.returnPath != nullptr && fileSel.sizeReturnPath == 0)
+ {
+ // Old versions of reViSiT (which still relied on the host's file selector) seem to be dodgy.
+ // They report a path size of 0, but when using an own buffer, they will crash.
+ // So we'll just assume that reViSiT can handle long enough (_MAX_PATH) paths here.
+ fileSel.sizeReturnPath = static_cast<int32>(strlen(fileSelResult.returnPath) + 1);
+ }
+ }
+ fileSel.numReturnPaths = fileSelResult.numReturnPaths;
+ fileSel.reserved = 1;
+ if(fileSel.command == kVstMultipleFilesLoad)
+ {
+ fileSel.returnMultiplePaths = fileSelResult.returnMultiplePaths;
+ } else if(fileSel.returnPath != nullptr && fileSel.sizeReturnPath != 0)
+ {
+ // Plugin provides memory
+ const auto len = strnlen(fileSelResult.returnPath, fileSel.sizeReturnPath - 1);
+ strncpy(fileSel.returnPath, fileSelResult.returnPath, len);
+ fileSel.returnPath[len] = '\0';
+ fileSel.reserved = 0;
+ } else
+ {
+ fileSel.returnPath = fileSelResult.returnPath;
+ fileSel.sizeReturnPath = fileSelResult.sizeReturnPath;
+ }
+ }
+ break;
+
+ case audioMasterGetChunkFile:
+ // Name in [ptr]
+ strcpy(ptrC, extraData);
+ break;
+ }
+
+ return static_cast<intptr_t>(resultMsg->result);
+}
+
+
+// Helper function for sending messages to the host.
+intptr_t VSTCALLBACK PluginBridge::MasterCallback(AEffect *effect, VstOpcodeToHost opcode, int32 index, intptr_t value, void *ptr, float opt)
+{
+ PluginBridge *instance = (effect != nullptr && effect->reservedForHost1 != nullptr) ? static_cast<PluginBridge *>(effect->reservedForHost1) : PluginBridge::m_latestInstance;
+ return instance->DispatchToHost(opcode, index, value, ptr, opt);
+}
+
+
+LRESULT CALLBACK PluginBridge::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ PluginBridge *that = nullptr;
+ if(hwnd == m_communicationWindow && wParam < m_plugins.size())
+ that = static_cast<PluginBridge *>(m_plugins[wParam]);
+ else // Editor windows
+ that = reinterpret_cast<PluginBridge *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+
+ if(that == nullptr)
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+
+ switch(uMsg)
+ {
+ case WM_ERASEBKGND:
+ // Pretend that we erased the background
+ return 1;
+
+ case WM_SIZE:
+ {
+ // For plugins that change their size but do not notify the host, e.g. Roland D-50
+ RECT rect{0, 0, 0, 0};
+ GetClientRect(hwnd, &rect);
+ const int width = rect.right - rect.left, height = rect.bottom - rect.top;
+ if(width > 0 && height > 0 && (width != that->m_windowWidth || height != that->m_windowHeight))
+ {
+ that->m_windowWidth = width;
+ that->m_windowHeight = height;
+ that->DispatchToHost(audioMasterSizeWindow, width, height, nullptr, 0.0f);
+ }
+ break;
+ }
+
+ case WM_BRIDGE_MESSAGE_TO_BRIDGE:
+ that->ParseNextMessage(static_cast<int>(lParam));
+ return WM_BRIDGE_SUCCESS;
+
+ case WM_BRIDGE_DELETE_PLUGIN:
+ delete that;
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+
+void PluginBridge::IdleTimerProc(HWND, UINT, UINT_PTR idTimer, DWORD)
+{
+ if(idTimer != TIMER_IDLE)
+ return;
+ for(auto *plugin : m_plugins)
+ {
+ auto *that = static_cast<PluginBridge *>(plugin);
+ if(that == nullptr)
+ continue;
+ if(that->m_needIdle)
+ {
+ that->Dispatch(effIdle, 0, 0, nullptr, 0.0f);
+ that->m_needIdle = false;
+ }
+ if(that->m_window)
+ {
+ that->Dispatch(effEditIdle, 0, 0, nullptr, 0.0f);
+ }
+ }
+}
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.h b/Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.h
new file mode 100644
index 00000000..3967edcc
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.h
@@ -0,0 +1,104 @@
+/*
+ * Bridge.h
+ * --------
+ * Purpose: VST plugin bridge (plugin side)
+ * 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 <atomic>
+
+#include "BridgeCommon.h"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+class PluginBridge : private BridgeCommon
+{
+public:
+ static bool m_fullMemDump;
+
+protected:
+ enum TimerID : UINT
+ {
+ TIMER_IDLE = 1,
+ };
+
+ static PluginBridge *m_latestInstance;
+ static ATOM m_editorClassAtom;
+
+ // Plugin
+ Vst::AEffect *m_nativeEffect = nullptr;
+ HMODULE m_library = nullptr;
+ HWND m_window = nullptr;
+ int m_windowWidth = 0, m_windowHeight = 0;
+ std::atomic<bool> m_isProcessing = false;
+
+ // Static memory for host-to-plugin pointers
+ union
+ {
+ Vst::VstSpeakerArrangement speakerArrangement;
+ char name[256];
+ } m_host2PlugMem;
+ std::vector<char> m_eventCache; // Cached VST (MIDI) events
+ std::vector<char> m_fileSelectCache; // Cached VstFileSelect data
+
+ // Pointers to sample data
+ std::vector<void *> m_sampleBuffers;
+ uint32 m_mixBufSize = 0;
+
+ HANDLE m_audioThread = nullptr;
+ Event m_sigThreadExit; // Signal to kill audio thread
+
+ bool m_needIdle = false; // Plugin needs idle time
+
+public:
+ PluginBridge(const wchar_t *memName, HANDLE otherProcess);
+ ~PluginBridge();
+
+ PluginBridge(const PluginBridge&) = delete;
+ PluginBridge &operator=(const PluginBridge&) = delete;
+
+ static void MainLoop(TCHAR *argv[]);
+
+protected:
+ void RequestDelete();
+
+ bool SendToHost(BridgeMessage &sendMsg);
+
+ void UpdateEffectStruct();
+ void CreateProcessingFile(std::vector<char> &dispatchData);
+
+ void ParseNextMessage(int msgID);
+ void NewInstance(NewInstanceMsg &msg);
+ void InitBridge(InitMsg &msg);
+ void DispatchToPlugin(DispatchMsg &msg);
+ void SetParameter(ParameterMsg &msg);
+ void GetParameter(ParameterMsg &msg);
+ void AutomateParameters();
+ void Process();
+ void ProcessReplacing();
+ void ProcessDoubleReplacing();
+ intptr_t DispatchToHost(Vst::VstOpcodeToHost opcode, int32 index, intptr_t value, void *ptr, float opt);
+ void SendErrorMessage(const wchar_t *str);
+ intptr_t Dispatch(Vst::VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt);
+
+ template<typename buf_t>
+ int32 BuildProcessPointers(buf_t **(&inPointers), buf_t **(&outPointers));
+
+ static DWORD WINAPI AudioThread(LPVOID param);
+ void AudioThread();
+
+ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ static void CALLBACK IdleTimerProc(HWND hwnd, UINT message, UINT_PTR idTimer, DWORD dwTime);
+
+ static intptr_t VSTCALLBACK MasterCallback(Vst::AEffect *effect, Vst::VstOpcodeToHost, int32 index, intptr_t value, void *ptr, float opt);
+};
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeCommon.h b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeCommon.h
new file mode 100644
index 00000000..405bd9be
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeCommon.h
@@ -0,0 +1,679 @@
+/*
+ * 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
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeOpCodes.h b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeOpCodes.h
new file mode 100644
index 00000000..e527e3f9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeOpCodes.h
@@ -0,0 +1,46 @@
+/*
+ * BridgeOpCodes.h
+ * ---------------
+ * Purpose: Various dispatch opcodes for communication between OpenMPT and its plugin bridge.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (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 "../mptrack/plugins/VstDefinitions.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+enum VendorSpecificOpCodes : int32
+{
+ // Explicitely tell the plugin bridge to update the AEffect struct cache
+ kUpdateEffectStruct = 0,
+ // Notify the host that it needs to resize its processing buffers
+ kUpdateProcessingBuffer,
+ // Returns the pointer to the bridge wrapper object for the current plugin
+ kGetWrapperPointer,
+ // Sets name for new shared memory object - name in ptr
+ kUpdateEventMemName,
+ kCloseOldProcessingMemory,
+ // Cache program names - ptr points to 2x int32 containing the start and end program index - caches names in range [start, end[
+ kCacheProgramNames,
+ // Cache parameter information - ptr points to 2x int32 containing the start and end parameter index - caches info in range [start, end[
+ kCacheParameterInfo,
+ // Cache parameter values
+ kBeginGetProgram,
+ // Stop using parameter value cache
+ kEndGetProgram,
+
+ // Constant for identifying our vendor-specific opcodes
+ kVendorOpenMPT = Vst::FourCC("OMPT"),
+
+ // Maximum length of cached program names
+ kCachedProgramNameLength = 256,
+};
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.cpp b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.cpp
new file mode 100644
index 00000000..4693a323
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.cpp
@@ -0,0 +1,1291 @@
+/*
+ * BridgeWrapper.cpp
+ * -----------------
+ * Purpose: VST plugin bridge wrapper (host side)
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+
+#ifdef MPT_WITH_VST
+#include "BridgeWrapper.h"
+#include "../soundlib/plugins/PluginManager.h"
+#include "../mptrack/Mainfrm.h"
+#include "../mptrack/Mptrack.h"
+#include "../mptrack/Vstplug.h"
+#include "../mptrack/ExceptionHandler.h"
+#include "../common/mptFileIO.h"
+#include "../common/mptStringBuffer.h"
+#include "../common/misc_util.h"
+
+using namespace Vst;
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+std::vector<BridgeCommon *> BridgeCommon::m_plugins;
+HWND BridgeCommon::m_communicationWindow = nullptr;
+int BridgeCommon::m_instanceCount = 0;
+thread_local bool BridgeCommon::m_isAudioThread = false;
+
+std::size_t GetPluginArchPointerSize(PluginArch arch)
+{
+ std::size_t result = 0;
+ switch(arch)
+ {
+ case PluginArch_x86:
+ result = 4;
+ break;
+ case PluginArch_amd64:
+ result = 8;
+ break;
+ case PluginArch_arm:
+ result = 4;
+ break;
+ case PluginArch_arm64:
+ result = 8;
+ break;
+ default:
+ result = 0;
+ break;
+ }
+ return result;
+}
+
+
+ComponentPluginBridge::ComponentPluginBridge(PluginArch arch, Generation generation)
+ : ComponentBase(ComponentTypeBundled)
+ , arch(arch)
+ , generation(generation)
+{
+}
+
+
+bool ComponentPluginBridge::DoInitialize()
+{
+ mpt::PathString archName;
+ switch(arch)
+ {
+ case PluginArch_x86:
+ if(mpt::OS::Windows::HostCanRun(mpt::OS::Windows::GetHostArchitecture(), mpt::OS::Windows::Architecture::x86) == mpt::OS::Windows::EmulationLevel::NA)
+ {
+ return false;
+ }
+ archName = P_("x86");
+ break;
+ case PluginArch_amd64:
+ if(mpt::OS::Windows::HostCanRun(mpt::OS::Windows::GetHostArchitecture(), mpt::OS::Windows::Architecture::amd64) == mpt::OS::Windows::EmulationLevel::NA)
+ {
+ return false;
+ }
+ archName = P_("amd64");
+ break;
+ case PluginArch_arm:
+ if(mpt::OS::Windows::HostCanRun(mpt::OS::Windows::GetHostArchitecture(), mpt::OS::Windows::Architecture::arm) == mpt::OS::Windows::EmulationLevel::NA)
+ {
+ return false;
+ }
+ archName = P_("arm");
+ break;
+ case PluginArch_arm64:
+ if(mpt::OS::Windows::HostCanRun(mpt::OS::Windows::GetHostArchitecture(), mpt::OS::Windows::Architecture::arm64) == mpt::OS::Windows::EmulationLevel::NA)
+ {
+ return false;
+ }
+ archName = P_("arm64");
+ break;
+ default:
+ break;
+ }
+ if(archName.empty())
+ {
+ return false;
+ }
+ exeName = mpt::PathString();
+ const mpt::PathString generationSuffix = (generation == Generation::Legacy) ? P_("Legacy") : P_("");
+ const mpt::PathString exeNames[] =
+ {
+ theApp.GetInstallPath() + P_("PluginBridge") + generationSuffix + P_("-") + archName + P_(".exe"), // Local
+ theApp.GetInstallBinPath() + archName + P_("\\") + P_("PluginBridge") + generationSuffix + P_(".exe"), // Multi-arch
+ theApp.GetInstallBinPath() + archName + P_("\\") + P_("PluginBridge") + generationSuffix + P_("-") + archName + P_(".exe") // Multi-arch transitional
+ };
+ for(const auto &candidate : exeNames)
+ {
+ if(candidate.IsFile())
+ {
+ exeName = candidate;
+ break;
+ }
+ }
+ if(exeName.empty())
+ {
+ availability = AvailabilityMissing;
+ return false;
+ }
+ std::vector<WCHAR> exePath(MAX_PATH);
+ while(GetModuleFileNameW(0, exePath.data(), mpt::saturate_cast<DWORD>(exePath.size())) >= exePath.size())
+ {
+ exePath.resize(exePath.size() * 2);
+ }
+ uint64 mptVersion = BridgeWrapper::GetFileVersion(exePath.data());
+ uint64 bridgeVersion = BridgeWrapper::GetFileVersion(exeName.ToWide().c_str());
+ if(bridgeVersion != mptVersion)
+ {
+ availability = AvailabilityWrongVersion;
+ return false;
+ }
+ availability = AvailabilityOK;
+ return true;
+}
+
+
+PluginArch BridgeWrapper::GetNativePluginBinaryType()
+{
+ PluginArch result = PluginArch_unknown;
+ switch(mpt::OS::Windows::GetProcessArchitecture())
+ {
+ case mpt::OS::Windows::Architecture::x86:
+ result = PluginArch_x86;
+ break;
+ case mpt::OS::Windows::Architecture::amd64:
+ result = PluginArch_amd64;
+ break;
+ case mpt::OS::Windows::Architecture::arm:
+ result = PluginArch_arm;
+ break;
+ case mpt::OS::Windows::Architecture::arm64:
+ result = PluginArch_arm64;
+ break;
+ default:
+ result = PluginArch_unknown;
+ break;
+ }
+ return result;
+}
+
+
+// Check whether we need to load a 32-bit or 64-bit wrapper.
+PluginArch BridgeWrapper::GetPluginBinaryType(const mpt::PathString &pluginPath)
+{
+ PluginArch type = PluginArch_unknown;
+ mpt::ifstream file(pluginPath, std::ios::in | std::ios::binary);
+ if(file.is_open())
+ {
+ IMAGE_DOS_HEADER dosHeader;
+ IMAGE_NT_HEADERS ntHeader;
+ file.read(reinterpret_cast<char *>(&dosHeader), sizeof(dosHeader));
+ if(dosHeader.e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ file.seekg(dosHeader.e_lfanew);
+ file.read(reinterpret_cast<char *>(&ntHeader), sizeof(ntHeader));
+
+ MPT_ASSERT((ntHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0);
+ switch(ntHeader.FileHeader.Machine)
+ {
+ case IMAGE_FILE_MACHINE_I386:
+ type = PluginArch_x86;
+ break;
+ case IMAGE_FILE_MACHINE_AMD64:
+ type = PluginArch_amd64;
+ break;
+#if defined(MPT_WITH_WINDOWS10)
+ case IMAGE_FILE_MACHINE_ARM:
+ type = PluginArch_arm;
+ break;
+ case IMAGE_FILE_MACHINE_ARM64:
+ type = PluginArch_arm64;
+ break;
+#endif // MPT_WITH_WINDOWS10
+ default:
+ type = PluginArch_unknown;
+ break;
+ }
+ }
+ }
+ return type;
+}
+
+
+uint64 BridgeWrapper::GetFileVersion(const WCHAR *exePath)
+{
+ DWORD verHandle = 0;
+ DWORD verSize = GetFileVersionInfoSizeW(exePath, &verHandle);
+ uint64 result = 0;
+ if(verSize == 0)
+ {
+ return result;
+ }
+
+ char *verData = new(std::nothrow) char[verSize];
+ if(verData && GetFileVersionInfoW(exePath, verHandle, verSize, verData))
+ {
+ UINT size = 0;
+ void *lpBuffer = nullptr;
+ if(VerQueryValue(verData, _T("\\"), &lpBuffer, &size) && size != 0)
+ {
+ auto *verInfo = static_cast<const VS_FIXEDFILEINFO *>(lpBuffer);
+ if(verInfo->dwSignature == 0xfeef04bd)
+ {
+ result = (uint64(HIWORD(verInfo->dwFileVersionMS)) << 48)
+ | (uint64(LOWORD(verInfo->dwFileVersionMS)) << 32)
+ | (uint64(HIWORD(verInfo->dwFileVersionLS)) << 16)
+ | uint64(LOWORD(verInfo->dwFileVersionLS));
+ }
+ }
+ }
+ delete[] verData;
+ return result;
+}
+
+
+// Create a plugin bridge object
+AEffect *BridgeWrapper::Create(const VSTPluginLib &plugin, bool forceLegacy)
+{
+ BridgeWrapper *wrapper = new(std::nothrow) BridgeWrapper();
+ BridgeWrapper *sharedInstance = nullptr;
+ const Generation wantedGeneration = (plugin.modernBridge && !forceLegacy) ? Generation::Modern : Generation::Legacy;
+
+ // Should we share instances?
+ if(plugin.shareBridgeInstance)
+ {
+ // Well, then find some instance to share with!
+ CVstPlugin *vstPlug = dynamic_cast<CVstPlugin *>(plugin.pPluginsList);
+ while(vstPlug != nullptr)
+ {
+ if(vstPlug->isBridged)
+ {
+ BridgeWrapper *instance = FromIntPtr<BridgeWrapper>(vstPlug->Dispatch(effVendorSpecific, kVendorOpenMPT, kGetWrapperPointer, nullptr, 0.0f));
+ if(wantedGeneration == instance->m_Generation)
+ {
+ sharedInstance = instance;
+ break;
+ }
+ }
+ vstPlug = dynamic_cast<CVstPlugin *>(vstPlug->GetNextInstance());
+ }
+ }
+
+ try
+ {
+ if(wrapper != nullptr && wrapper->Init(plugin.dllPath, wantedGeneration, sharedInstance) && wrapper->m_queueMem.Good())
+ {
+ return &wrapper->m_sharedMem->effect;
+ }
+ delete wrapper;
+ return nullptr;
+ } catch(BridgeException &)
+ {
+ delete wrapper;
+ throw;
+ }
+}
+
+
+BridgeWrapper::BridgeWrapper()
+{
+ m_thisPluginID = static_cast<int32>(m_plugins.size());
+ m_plugins.push_back(this);
+
+ if(m_instanceCount == 1)
+ CreateCommunicationWindow(WindowProc);
+}
+
+
+BridgeWrapper::~BridgeWrapper()
+{
+ if(m_instanceCount == 1)
+ DestroyWindow(m_communicationWindow);
+}
+
+
+// Initialize and launch bridge
+bool BridgeWrapper::Init(const mpt::PathString &pluginPath, Generation bridgeGeneration, BridgeWrapper *sharedInstace)
+{
+ static uint32 plugId = 0;
+ plugId++;
+ const DWORD procId = GetCurrentProcessId();
+
+ const std::wstring mapName = MPT_WFORMAT("Local\\openmpt-{}-{}")(procId, plugId);
+
+ // Create our shared memory object.
+ if(!m_queueMem.Create(mapName.c_str(), sizeof(SharedMemLayout))
+ || !CreateSignals(mapName.c_str()))
+ {
+ throw BridgeException("Could not initialize plugin bridge memory.");
+ }
+ m_sharedMem = m_queueMem.Data<SharedMemLayout>();
+
+ if(sharedInstace == nullptr)
+ {
+ // Create a new bridge instance
+ const PluginArch arch = GetPluginBinaryType(pluginPath);
+ bool available = false;
+ ComponentPluginBridge::Availability availability = ComponentPluginBridge::AvailabilityUnknown;
+ switch(arch)
+ {
+ case PluginArch_x86:
+ if(available = (bridgeGeneration == Generation::Modern) ? IsComponentAvailable(pluginBridge_x86) : IsComponentAvailable(pluginBridgeLegacy_x86); !available)
+ availability = (bridgeGeneration == Generation::Modern) ? pluginBridge_x86->GetAvailability() : pluginBridgeLegacy_x86->GetAvailability();
+ break;
+ case PluginArch_amd64:
+ if(available = (bridgeGeneration == Generation::Modern) ? IsComponentAvailable(pluginBridge_amd64) : IsComponentAvailable(pluginBridgeLegacy_amd64); !available)
+ availability = (bridgeGeneration == Generation::Modern) ? pluginBridge_amd64->GetAvailability() : pluginBridgeLegacy_amd64->GetAvailability();
+ break;
+#if defined(MPT_WITH_WINDOWS10)
+ case PluginArch_arm:
+ if(available = (bridgeGeneration == Generation::Modern) ? IsComponentAvailable(pluginBridge_arm) : IsComponentAvailable(pluginBridgeLegacy_arm); !available)
+ availability = (bridgeGeneration == Generation::Modern) ? pluginBridge_arm->GetAvailability() : pluginBridgeLegacy_arm->GetAvailability();
+ break;
+ case PluginArch_arm64:
+ if(available = (bridgeGeneration == Generation::Modern) ? IsComponentAvailable(pluginBridge_arm64) : IsComponentAvailable(pluginBridgeLegacy_arm64); !available)
+ availability = (bridgeGeneration == Generation::Modern) ? pluginBridge_arm64->GetAvailability() : pluginBridgeLegacy_arm64->GetAvailability();
+ break;
+#endif // MPT_WITH_WINDOWS10
+ default:
+ break;
+ }
+ if(arch == PluginArch_unknown)
+ {
+ return false;
+ }
+ if(!available)
+ {
+ switch(availability)
+ {
+ case ComponentPluginBridge::AvailabilityMissing:
+ // Silently fail if bridge is missing.
+ throw BridgeNotFoundException();
+ break;
+ case ComponentPluginBridge::AvailabilityWrongVersion:
+ throw BridgeException("The plugin bridge version does not match your OpenMPT version.");
+ break;
+ default:
+ throw BridgeNotFoundException();
+ break;
+ }
+ }
+ const ComponentPluginBridge *const pluginBridge =
+ (arch == PluginArch_x86 && bridgeGeneration == Generation::Modern) ? static_cast<const ComponentPluginBridge *>(pluginBridge_x86.get()) :
+ (arch == PluginArch_x86 && bridgeGeneration == Generation::Legacy) ? static_cast<const ComponentPluginBridge *>(pluginBridgeLegacy_x86.get()) :
+ (arch == PluginArch_amd64 && bridgeGeneration == Generation::Modern) ? static_cast<const ComponentPluginBridge *>(pluginBridge_amd64.get()) :
+ (arch == PluginArch_amd64 && bridgeGeneration == Generation::Legacy) ? static_cast<const ComponentPluginBridge *>(pluginBridgeLegacy_amd64.get()) :
+#if defined(MPT_WITH_WINDOWS10)
+ (arch == PluginArch_arm && bridgeGeneration == Generation::Modern) ? static_cast<const ComponentPluginBridge *>(pluginBridge_arm.get()) :
+ (arch == PluginArch_arm && bridgeGeneration == Generation::Legacy) ? static_cast<const ComponentPluginBridge *>(pluginBridgeLegacy_arm.get()) :
+ (arch == PluginArch_arm64 && bridgeGeneration == Generation::Modern) ? static_cast<const ComponentPluginBridge *>(pluginBridge_arm64.get()) :
+ (arch == PluginArch_arm64 && bridgeGeneration == Generation::Legacy) ? static_cast<const ComponentPluginBridge *>(pluginBridgeLegacy_arm64.get()) :
+#endif // MPT_WITH_WINDOWS10
+ nullptr;
+ if(!pluginBridge)
+ {
+ return false;
+ }
+ m_Generation = bridgeGeneration;
+ const mpt::PathString exeName = pluginBridge->GetFileName();
+
+ m_otherPtrSize = static_cast<int32>(GetPluginArchPointerSize(arch));
+
+ std::wstring cmdLine = MPT_WFORMAT("{} {}")(mapName, procId);
+
+ STARTUPINFOW info;
+ MemsetZero(info);
+ info.cb = sizeof(info);
+ PROCESS_INFORMATION processInfo;
+ MemsetZero(processInfo);
+
+ if(!CreateProcessW(exeName.ToWide().c_str(), cmdLine.data(), NULL, NULL, FALSE, 0, NULL, NULL, &info, &processInfo))
+ {
+ throw BridgeException("Failed to launch plugin bridge.");
+ }
+ CloseHandle(processInfo.hThread);
+ m_otherProcess = processInfo.hProcess;
+ } else
+ {
+ // Re-use existing bridge instance
+ m_otherPtrSize = sharedInstace->m_otherPtrSize;
+ m_otherProcess.DuplicateFrom(sharedInstace->m_otherProcess);
+
+ BridgeMessage msg;
+ msg.NewInstance(mapName.c_str());
+ if(!sharedInstace->SendToBridge(msg))
+ {
+ // Something went wrong, try a new instance
+ return Init(pluginPath, bridgeGeneration, nullptr);
+ }
+ }
+
+ // Initialize bridge
+ m_sharedMem->effect.object = this;
+ m_sharedMem->effect.dispatcher = DispatchToPlugin;
+ m_sharedMem->effect.setParameter = SetParameter;
+ m_sharedMem->effect.getParameter = GetParameter;
+ m_sharedMem->effect.process = Process;
+ std::memcpy(&(m_sharedMem->effect.reservedForHost2), "OMPT", 4);
+
+ m_sigAutomation.Create(true);
+
+ m_sharedMem->hostCommWindow = m_communicationWindow;
+
+ const HANDLE objects[] = {m_sigBridgeReady, m_otherProcess};
+ if(WaitForMultipleObjects(mpt::saturate_cast<DWORD>(std::size(objects)), objects, FALSE, 10000) != WAIT_OBJECT_0)
+ {
+ throw BridgeException("Could not connect to plugin bridge, it probably crashed.");
+ }
+ m_otherPluginID = m_sharedMem->bridgePluginID;
+
+ BridgeMessage initMsg;
+ initMsg.Init(pluginPath.ToWide().c_str(), MIXBUFFERSIZE, m_thisPluginID, ExceptionHandler::fullMemDump);
+
+ if(!SendToBridge(initMsg))
+ {
+ throw BridgeException("Could not initialize plugin bridge, it probably crashed.");
+ } else if(initMsg.init.result != 1)
+ {
+ throw BridgeException(mpt::ToCharset(mpt::Charset::UTF8, initMsg.init.str).c_str());
+ }
+
+ if(m_sharedMem->effect.flags & effFlagsCanReplacing)
+ m_sharedMem->effect.processReplacing = ProcessReplacing;
+ if(m_sharedMem->effect.flags & effFlagsCanDoubleReplacing)
+ m_sharedMem->effect.processDoubleReplacing = ProcessDoubleReplacing;
+ return true;
+}
+
+
+// Send an arbitrary message to the bridge.
+// Returns true if the message was processed by the bridge.
+bool BridgeWrapper::SendToBridge(BridgeMessage &sendMsg)
+{
+ const bool inAudioThread = m_isAudioThread || CMainFrame::GetMainFrame()->InAudioThread();
+ auto &messages = m_sharedMem->ipcMessages;
+ const auto msgID = CopyToSharedMemory(sendMsg, messages);
+ if(msgID < 0)
+ return false;
+ BridgeMessage &sharedMsg = messages[msgID];
+
+ if(!inAudioThread)
+ {
+ if(SendMessage(m_sharedMem->bridgeCommWindow, WM_BRIDGE_MESSAGE_TO_BRIDGE, m_otherPluginID, msgID) == WM_BRIDGE_SUCCESS)
+ {
+ sharedMsg.CopyTo(sendMsg);
+ return true;
+ }
+ return false;
+ }
+
+ // Audio thread: Use signals instead of window messages
+ m_sharedMem->audioThreadToBridgeMsgID = msgID;
+ m_sigToBridgeAudio.Send();
+
+ // Wait until we get the result from the bridge
+ DWORD result;
+ const HANDLE objects[] = {m_sigToBridgeAudio.confirm, m_sigToHostAudio.send, m_otherProcess};
+ do
+ {
+ result = WaitForMultipleObjects(mpt::saturate_cast<DWORD>(std::size(objects)), objects, FALSE, INFINITE);
+ if(result == WAIT_OBJECT_0)
+ {
+ // Message got answered
+ sharedMsg.CopyTo(sendMsg);
+ break;
+ } else if(result == WAIT_OBJECT_0 + 1)
+ {
+ ParseNextMessage(m_sharedMem->audioThreadToHostMsgID);
+ m_sigToHostAudio.Confirm();
+ }
+ } while(result != WAIT_OBJECT_0 + 2 && result != WAIT_FAILED);
+
+ return (result == WAIT_OBJECT_0);
+}
+
+
+// Receive a message from the host and translate it.
+void BridgeWrapper::ParseNextMessage(int msgID)
+{
+ auto &msg = m_sharedMem->ipcMessages[msgID];
+ switch(msg.header.type)
+ {
+ case MsgHeader::dispatch:
+ DispatchToHost(msg.dispatch);
+ break;
+
+ case MsgHeader::errorMsg:
+ // TODO Showing a message box here will deadlock as the main thread can be in a waiting state
+ //throw BridgeErrorException(msg.error.str);
+ break;
+ }
+}
+
+
+void BridgeWrapper::DispatchToHost(DispatchMsg &msg)
+{
+ // Various dispatch data - depending on the opcode, one of those might be used.
+ std::vector<char> extraData;
+
+ MappedMemory auxMem;
+
+ // Content of ptr is usually stored right after the message header, ptr field indicates size.
+ void *ptr = (msg.ptr != 0) ? (&msg + 1) : nullptr;
+ if(msg.size > sizeof(BridgeMessage))
+ {
+ if(!auxMem.Open(static_cast<const wchar_t *>(ptr)))
+ {
+ return;
+ }
+ ptr = auxMem.Data();
+ }
+ void *origPtr = ptr;
+
+ switch(msg.opcode)
+ {
+ case audioMasterProcessEvents:
+ // VstEvents* in [ptr]
+ TranslateBridgeToVstEvents(extraData, ptr);
+ ptr = extraData.data();
+ break;
+
+ case audioMasterVendorSpecific:
+ if(msg.index != kVendorOpenMPT || msg.value != kUpdateProcessingBuffer)
+ {
+ break;
+ }
+ [[fallthrough]];
+ case audioMasterIOChanged:
+ {
+ // If the song is playing, the rendering thread might be active at the moment,
+ // so we should keep the current processing memory alive until it is done for sure.
+ const CVstPlugin *plug = static_cast<CVstPlugin *>(m_sharedMem->effect.reservedForHost1);
+ const bool isPlaying = plug != nullptr && plug->IsResumed();
+ if(isPlaying)
+ {
+ m_oldProcessMem.CopyFrom(m_processMem);
+ }
+ // Set up new processing file
+ m_processMem.Open(static_cast<wchar_t *>(ptr));
+ if(isPlaying)
+ {
+ msg.result = 1;
+ return;
+ }
+ }
+ break;
+
+ case audioMasterUpdateDisplay:
+ m_cachedProgNames.clear();
+ m_cachedParamInfo.clear();
+ break;
+
+ case audioMasterOpenFileSelector:
+ TranslateBridgeToVstFileSelect(extraData, ptr, static_cast<size_t>(msg.ptr));
+ ptr = extraData.data();
+ break;
+ }
+
+ intptr_t result = CVstPlugin::MasterCallBack(&m_sharedMem->effect, static_cast<VstOpcodeToHost>(msg.opcode), msg.index, static_cast<intptr_t>(msg.value), ptr, msg.opt);
+ msg.result = static_cast<int32>(result);
+
+ // Post-fix some opcodes
+ switch(msg.opcode)
+ {
+ case audioMasterGetTime:
+ // VstTimeInfo* in [return value]
+ if(msg.result != 0)
+ {
+ m_sharedMem->timeInfo = *FromIntPtr<VstTimeInfo>(result);
+ }
+ break;
+
+ case audioMasterGetDirectory:
+ // char* in [return value]
+ if(msg.result != 0)
+ {
+ char *target = static_cast<char *>(ptr);
+ strncpy(target, FromIntPtr<const char>(result), static_cast<size_t>(msg.ptr - 1));
+ target[msg.ptr - 1] = 0;
+ }
+ break;
+
+ case audioMasterOpenFileSelector:
+ if(msg.result != 0)
+ {
+ std::vector<char> fileSelect;
+ TranslateVstFileSelectToBridge(fileSelect, *static_cast<const VstFileSelect *>(ptr), m_otherPtrSize);
+ std::memcpy(origPtr, fileSelect.data(), std::min(fileSelect.size(), static_cast<size_t>(msg.ptr)));
+ // Directly free memory on host side, we don't need it anymore
+ CVstPlugin::MasterCallBack(&m_sharedMem->effect, audioMasterCloseFileSelector, msg.index, static_cast<intptr_t>(msg.value), ptr, msg.opt);
+ }
+ break;
+ }
+}
+
+
+intptr_t VSTCALLBACK BridgeWrapper::DispatchToPlugin(AEffect *effect, VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt)
+{
+ BridgeWrapper *that = static_cast<BridgeWrapper *>(effect->object);
+ if(that != nullptr)
+ {
+ return that->DispatchToPlugin(opcode, index, value, ptr, opt);
+ }
+ return 0;
+}
+
+
+intptr_t BridgeWrapper::DispatchToPlugin(VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt)
+{
+ std::vector<char> dispatchData(sizeof(DispatchMsg), 0);
+ int64 ptrOut = 0;
+ bool copyPtrBack = false, ptrIsSize = true;
+ char *ptrC = static_cast<char *>(ptr);
+
+ switch(opcode)
+ {
+ case effGetParamLabel:
+ case effGetParamDisplay:
+ case effGetParamName:
+ if(index >= m_cachedParamInfoStart && index < m_cachedParamInfoStart + mpt::saturate_cast<int32>(m_cachedParamInfo.size()))
+ {
+ if(opcode == effGetParamLabel)
+ strcpy(ptrC, m_cachedParamInfo[index - m_cachedParamInfoStart].label);
+ else if(opcode == effGetParamDisplay)
+ strcpy(ptrC, m_cachedParamInfo[index - m_cachedParamInfoStart].display);
+ else if(opcode == effGetParamName)
+ strcpy(ptrC, m_cachedParamInfo[index - m_cachedParamInfoStart].name);
+ return 1;
+ }
+ [[fallthrough]];
+ case effGetProgramName:
+ case effString2Parameter:
+ case effGetProgramNameIndexed:
+ case effGetEffectName:
+ case effGetErrorText:
+ case effGetVendorString:
+ case effGetProductString:
+ case effShellGetNextPlugin:
+ // Name in [ptr]
+ if(opcode == effGetProgramNameIndexed && !m_cachedProgNames.empty())
+ {
+ // First check if we have cached this program name
+ if(index >= m_cachedProgNameStart && index < m_cachedProgNameStart + mpt::saturate_cast<int32>(m_cachedProgNames.size() / kCachedProgramNameLength))
+ {
+ strcpy(ptrC, &m_cachedProgNames[(index - m_cachedProgNameStart) * kCachedProgramNameLength]);
+ return 1;
+ }
+ }
+ ptrOut = 256;
+ copyPtrBack = true;
+ break;
+
+ case effSetProgramName:
+ m_cachedProgNames.clear();
+ [[fallthrough]];
+ case effCanDo:
+ // char* in [ptr]
+ ptrOut = strlen(ptrC) + 1;
+ dispatchData.insert(dispatchData.end(), ptrC, ptrC + ptrOut);
+ break;
+
+ case effIdle:
+ // The plugin bridge will generate these messages by itself
+ return 0;
+
+ case effEditGetRect:
+ // ERect** in [ptr]
+ ptrOut = sizeof(ERect);
+ copyPtrBack = true;
+ break;
+
+ case effEditOpen:
+ // HWND in [ptr] - Note: Window handles are interoperable between 32-bit and 64-bit applications in Windows (http://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx)
+ ptrOut = reinterpret_cast<int64>(ptr);
+ ptrIsSize = false;
+ m_cachedProgNames.clear();
+ m_cachedParamInfo.clear();
+ break;
+
+ case effEditIdle:
+ // The plugin bridge will generate these messages by itself
+ return 0;
+
+ case effGetChunk:
+ // void** in [ptr] for chunk data address
+ {
+ static uint32 chunkId = 0;
+ const std::wstring mapName = L"Local\\openmpt-" + mpt::wfmt::val(GetCurrentProcessId()) + L"-chunkdata-" + mpt::wfmt::val(chunkId++);
+ ptrOut = (mapName.length() + 1) * sizeof(wchar_t);
+ PushToVector(dispatchData, *mapName.c_str(), static_cast<size_t>(ptrOut));
+ }
+ break;
+
+ case effSetChunk:
+ // void* in [ptr] for chunk data
+ ptrOut = value;
+ dispatchData.insert(dispatchData.end(), ptrC, ptrC + value);
+ m_cachedProgNames.clear();
+ m_cachedParamInfo.clear();
+ break;
+
+ case effProcessEvents:
+ // VstEvents* in [ptr]
+ // We process in a separate memory segment to save a bridge communication message.
+ {
+ std::vector<char> events;
+ TranslateVstEventsToBridge(events, *static_cast<VstEvents *>(ptr), m_otherPtrSize);
+ if(m_eventMem.Size() < events.size())
+ {
+ // Resize memory
+ static uint32 chunkId = 0;
+ const std::wstring mapName = L"Local\\openmpt-" + mpt::wfmt::val(GetCurrentProcessId()) + L"-events-" + mpt::wfmt::val(chunkId++);
+ ptrOut = (mapName.length() + 1) * sizeof(wchar_t);
+ PushToVector(dispatchData, *mapName.c_str(), static_cast<size_t>(ptrOut));
+ m_eventMem.Create(mapName.c_str(), static_cast<uint32>(events.size() + 1024));
+
+ opcode = effVendorSpecific;
+ index = kVendorOpenMPT;
+ value = kUpdateEventMemName;
+ }
+ std::memcpy(m_eventMem.Data(), events.data(), events.size());
+ }
+ if(opcode != effVendorSpecific)
+ {
+ return 1;
+ }
+ break;
+
+ case effGetInputProperties:
+ case effGetOutputProperties:
+ // VstPinProperties* in [ptr]
+ ptrOut = sizeof(VstPinProperties);
+ copyPtrBack = true;
+ break;
+
+ case effOfflineNotify:
+ // VstAudioFile* in [ptr]
+ ptrOut = sizeof(VstAudioFile) * value;
+ // TODO
+ return 0;
+ break;
+
+ case effOfflinePrepare:
+ case effOfflineRun:
+ // VstOfflineTask* in [ptr]
+ ptrOut = sizeof(VstOfflineTask) * value;
+ // TODO
+ return 0;
+ break;
+
+ case effProcessVarIo:
+ // VstVariableIo* in [ptr]
+ ptrOut = sizeof(VstVariableIo);
+ // TODO
+ return 0;
+ break;
+
+ case effSetSpeakerArrangement:
+ // VstSpeakerArrangement* in [value] and [ptr]
+ ptrOut = sizeof(VstSpeakerArrangement) * 2;
+ PushToVector(dispatchData, *static_cast<VstSpeakerArrangement *>(ptr));
+ PushToVector(dispatchData, *FromIntPtr<VstSpeakerArrangement>(value));
+ break;
+
+ case effVendorSpecific:
+ if(index == kVendorOpenMPT)
+ {
+ switch(value)
+ {
+ case kGetWrapperPointer:
+ return ToIntPtr<BridgeWrapper>(this);
+
+ case kCloseOldProcessingMemory:
+ {
+ intptr_t result = m_oldProcessMem.Good();
+ m_oldProcessMem.Close();
+ return result;
+ }
+
+ case kCacheProgramNames:
+ {
+ int32 *prog = static_cast<int32 *>(ptr);
+ m_cachedProgNameStart = prog[0];
+ ptrOut = std::max(static_cast<int64>(sizeof(int32) * 2), static_cast<int64>((prog[1] - prog[0]) * kCachedProgramNameLength));
+ dispatchData.insert(dispatchData.end(), ptrC, ptrC + 2 * sizeof(int32));
+ }
+ break;
+
+ case kCacheParameterInfo:
+ {
+ int32 *param = static_cast<int32 *>(ptr);
+ m_cachedParamInfoStart = param[0];
+ ptrOut = std::max(static_cast<int64>(sizeof(int32) * 2), static_cast<int64>((param[1] - param[0]) * sizeof(ParameterInfo)));
+ dispatchData.insert(dispatchData.end(), ptrC, ptrC + 2 * sizeof(int32));
+ }
+ break;
+
+ case kBeginGetProgram:
+ ptrOut = m_sharedMem->effect.numParams * sizeof(float);
+ break;
+
+ case kEndGetProgram:
+ m_cachedParamValues.clear();
+ return 1;
+ }
+ }
+ break;
+
+ case effGetTailSize:
+ return m_sharedMem->tailSize;
+
+ case effGetParameterProperties:
+ // VstParameterProperties* in [ptr]
+ if(index >= m_cachedParamInfoStart && index < m_cachedParamInfoStart + mpt::saturate_cast<int32>(m_cachedParamInfo.size()))
+ {
+ *static_cast<VstParameterProperties *>(ptr) = m_cachedParamInfo[index - m_cachedParamInfoStart].props;
+ return 1;
+ }
+ ptrOut = sizeof(VstParameterProperties);
+ copyPtrBack = true;
+ break;
+
+ case effGetMidiProgramName:
+ case effGetCurrentMidiProgram:
+ // MidiProgramName* in [ptr]
+ ptrOut = sizeof(MidiProgramName);
+ copyPtrBack = true;
+ break;
+
+ case effGetMidiProgramCategory:
+ // MidiProgramCategory* in [ptr]
+ ptrOut = sizeof(MidiProgramCategory);
+ copyPtrBack = true;
+ break;
+
+ case effGetMidiKeyName:
+ // MidiKeyName* in [ptr]
+ ptrOut = sizeof(MidiKeyName);
+ copyPtrBack = true;
+ break;
+
+ case effBeginSetProgram:
+ m_isSettingProgram = true;
+ break;
+
+ case effEndSetProgram:
+ m_isSettingProgram = false;
+ if(m_sharedMem->automationQueue.pendingEvents)
+ {
+ SendAutomationQueue();
+ }
+ m_cachedProgNames.clear();
+ m_cachedParamInfo.clear();
+ break;
+
+ case effGetSpeakerArrangement:
+ // VstSpeakerArrangement* in [value] and [ptr]
+ ptrOut = sizeof(VstSpeakerArrangement) * 2;
+ copyPtrBack = true;
+ break;
+
+ case effBeginLoadBank:
+ case effBeginLoadProgram:
+ // VstPatchChunkInfo* in [ptr]
+ ptrOut = sizeof(VstPatchChunkInfo);
+ m_cachedProgNames.clear();
+ m_cachedParamInfo.clear();
+ break;
+
+ default:
+ MPT_ASSERT(ptr == nullptr);
+ }
+
+ if(ptrOut != 0 && ptrIsSize)
+ {
+ // In case we only reserve space and don't copy stuff over...
+ dispatchData.resize(sizeof(DispatchMsg) + static_cast<size_t>(ptrOut), 0);
+ }
+
+ uint32 extraSize = static_cast<uint32>(dispatchData.size() - sizeof(DispatchMsg));
+
+ // Create message header
+ BridgeMessage &msg = *reinterpret_cast<BridgeMessage *>(dispatchData.data());
+ msg.Dispatch(opcode, index, value, ptrOut, opt, extraSize);
+
+ const bool useAuxMem = dispatchData.size() > sizeof(BridgeMessage);
+ AuxMem *auxMem = nullptr;
+ if(useAuxMem)
+ {
+ // Extra data doesn't fit in message - use secondary memory
+ if(dispatchData.size() > std::numeric_limits<uint32>::max())
+ return 0;
+ auxMem = GetAuxMemory(mpt::saturate_cast<uint32>(dispatchData.size()));
+ if(auxMem == nullptr)
+ return 0;
+
+ // First, move message data to shared memory...
+ std::memcpy(auxMem->memory.Data(), &dispatchData[sizeof(DispatchMsg)], extraSize);
+ // ...Now put the shared memory name in the message instead.
+ std::memcpy(&dispatchData[sizeof(DispatchMsg)], auxMem->name, sizeof(auxMem->name));
+ }
+
+ try
+ {
+ if(!SendToBridge(msg) && opcode != effClose)
+ {
+ return 0;
+ }
+ } catch(...)
+ {
+ // Don't do anything for now.
+#if 0
+ if(opcode != effClose)
+ {
+ throw;
+ }
+#endif
+ }
+
+ const DispatchMsg &resultMsg = msg.dispatch;
+
+ // cppcheck false-positive
+ // cppcheck-suppress nullPointerRedundantCheck
+ const void *extraData = useAuxMem ? auxMem->memory.Data<const char>() : reinterpret_cast<const char *>(&resultMsg + 1);
+ // Post-fix some opcodes
+ switch(opcode)
+ {
+ case effClose:
+ m_sharedMem->effect.object = nullptr;
+ delete this;
+ return 0;
+
+ case effGetProgramName:
+ case effGetParamLabel:
+ case effGetParamDisplay:
+ case effGetParamName:
+ case effString2Parameter:
+ case effGetProgramNameIndexed:
+ case effGetEffectName:
+ case effGetErrorText:
+ case effGetVendorString:
+ case effGetProductString:
+ case effShellGetNextPlugin:
+ // Name in [ptr]
+ strcpy(ptrC, static_cast<const char *>(extraData));
+ break;
+
+ case effEditGetRect:
+ // ERect** in [ptr]
+ m_editRect = *static_cast<const ERect *>(extraData);
+ *static_cast<const ERect **>(ptr) = &m_editRect;
+ break;
+
+ case effGetChunk:
+ // void** in [ptr] for chunk data address
+ if(const wchar_t *str = static_cast<const wchar_t *>(extraData); m_getChunkMem.Open(str))
+ *static_cast<void **>(ptr) = m_getChunkMem.Data();
+ else
+ return 0;
+ break;
+
+ case effVendorSpecific:
+ if(index == kVendorOpenMPT && resultMsg.result == 1)
+ {
+ switch(value)
+ {
+ case kCacheProgramNames:
+ m_cachedProgNames.assign(static_cast<const char *>(extraData), static_cast<const char *>(extraData) + ptrOut);
+ break;
+ case kCacheParameterInfo:
+ {
+ const ParameterInfo *params = static_cast<const ParameterInfo *>(extraData);
+ m_cachedParamInfo.assign(params, params + ptrOut / sizeof(ParameterInfo));
+ break;
+ }
+ case kBeginGetProgram:
+ m_cachedParamValues.assign(static_cast<const float *>(extraData), static_cast<const float *>(extraData) + ptrOut / sizeof(float));
+ break;
+ }
+ }
+ break;
+
+ case effGetSpeakerArrangement:
+ // VstSpeakerArrangement* in [value] and [ptr]
+ m_speakers[0] = *static_cast<const VstSpeakerArrangement *>(extraData);
+ m_speakers[1] = *(static_cast<const VstSpeakerArrangement *>(extraData) + 1);
+ *static_cast<VstSpeakerArrangement *>(ptr) = m_speakers[0];
+ *FromIntPtr<VstSpeakerArrangement>(value) = m_speakers[1];
+ break;
+
+ default:
+ // TODO: Translate VstVariableIo, offline tasks
+ if(copyPtrBack)
+ {
+ std::memcpy(ptr, extraData, static_cast<size_t>(ptrOut));
+ }
+ }
+
+ if(auxMem != nullptr)
+ {
+ auxMem->used = false;
+ }
+
+ return static_cast<intptr_t>(resultMsg.result);
+}
+
+
+// Allocate auxiliary shared memory for too long bridge messages
+BridgeWrapper::AuxMem *BridgeWrapper::GetAuxMemory(uint32 size)
+{
+ std::size_t index = std::size(m_auxMems);
+ for(int pass = 0; pass < 2; pass++)
+ {
+ for(std::size_t i = 0; i < std::size(m_auxMems); i++)
+ {
+ if(m_auxMems[i].size >= size || pass == 1)
+ {
+ // Good candidate - is it taken yet?
+ bool expected = false;
+ if(m_auxMems[i].used.compare_exchange_strong(expected, true))
+ {
+ index = i;
+ break;
+ }
+ }
+ }
+ if(index != std::size(m_auxMems))
+ break;
+ }
+ if(index == std::size(m_auxMems))
+ return nullptr;
+
+ AuxMem &auxMem = m_auxMems[index];
+ if(auxMem.size >= size && auxMem.memory.Good())
+ {
+ // Re-use as-is
+ return &auxMem;
+ }
+ // Create new memory with appropriate size
+ static_assert(sizeof(DispatchMsg) + sizeof(auxMem.name) <= sizeof(BridgeMessage), "Check message sizes, this will crash!");
+ static unsigned int auxMemCount = 0;
+ mpt::String::WriteAutoBuf(auxMem.name) = MPT_WFORMAT("Local\\openmpt-{}-auxmem-{}")(GetCurrentProcessId(), auxMemCount++);
+ if(auxMem.memory.Create(auxMem.name, size))
+ {
+ auxMem.size = size;
+ return &auxMem;
+ } else
+ {
+ auxMem.used = false;
+ return nullptr;
+ }
+}
+
+
+// Send any pending automation events
+void BridgeWrapper::SendAutomationQueue()
+{
+ m_sigAutomation.Reset();
+ BridgeMessage msg;
+ msg.Automate();
+ if(!SendToBridge(msg))
+ {
+ // Failed (plugin probably crashed) - auto-fix event count
+ m_sharedMem->automationQueue.pendingEvents = 0;
+ }
+ m_sigAutomation.Trigger();
+}
+
+void VSTCALLBACK BridgeWrapper::SetParameter(AEffect *effect, int32 index, float parameter)
+{
+ BridgeWrapper *that = static_cast<BridgeWrapper *>(effect->object);
+ if(that)
+ {
+ try
+ {
+ that->SetParameter(index, parameter);
+ } catch(...)
+ {
+ // Be quiet about exceptions here
+ }
+ }
+}
+
+
+void BridgeWrapper::SetParameter(int32 index, float parameter)
+{
+ const CVstPlugin *plug = static_cast<CVstPlugin *>(m_sharedMem->effect.reservedForHost1);
+ AutomationQueue &autoQueue = m_sharedMem->automationQueue;
+ if(m_isSettingProgram || (plug && plug->IsResumed()))
+ {
+ // Queue up messages while rendering to reduce latency introduced by every single bridge call
+ uint32 i;
+ while((i = autoQueue.pendingEvents.fetch_add(1)) >= std::size(autoQueue.params))
+ {
+ // Queue full!
+ if(i == std::size(autoQueue.params))
+ {
+ // We're the first to notice that it's full
+ SendAutomationQueue();
+ } else
+ {
+ // Wait until queue is emptied by someone else (this branch is very unlikely to happen)
+ WaitForSingleObject(m_sigAutomation, INFINITE);
+ }
+ }
+
+ autoQueue.params[i].index = index;
+ autoQueue.params[i].value = parameter;
+ return;
+ } else if(autoQueue.pendingEvents)
+ {
+ // Actually, this should never happen as pending events are cleared before processing and at the end of a set program event.
+ SendAutomationQueue();
+ }
+
+ BridgeMessage msg;
+ msg.SetParameter(index, parameter);
+ SendToBridge(msg);
+}
+
+
+float VSTCALLBACK BridgeWrapper::GetParameter(AEffect *effect, int32 index)
+{
+ BridgeWrapper *that = static_cast<BridgeWrapper *>(effect->object);
+ if(that)
+ {
+ if(static_cast<size_t>(index) < that->m_cachedParamValues.size())
+ return that->m_cachedParamValues[index];
+
+ try
+ {
+ return that->GetParameter(index);
+ } catch(...)
+ {
+ // Be quiet about exceptions here
+ }
+ }
+ return 0.0f;
+}
+
+
+float BridgeWrapper::GetParameter(int32 index)
+{
+ BridgeMessage msg;
+ msg.GetParameter(index);
+ if(SendToBridge(msg))
+ {
+ return msg.parameter.value;
+ }
+ return 0.0f;
+}
+
+
+void VSTCALLBACK BridgeWrapper::Process(AEffect *effect, float **inputs, float **outputs, int32 sampleFrames)
+{
+ BridgeWrapper *that = static_cast<BridgeWrapper *>(effect->object);
+ if(sampleFrames != 0 && that != nullptr)
+ {
+ that->BuildProcessBuffer(ProcessMsg::process, effect->numInputs, effect->numOutputs, inputs, outputs, sampleFrames);
+ }
+}
+
+
+void VSTCALLBACK BridgeWrapper::ProcessReplacing(AEffect *effect, float **inputs, float **outputs, int32 sampleFrames)
+{
+ BridgeWrapper *that = static_cast<BridgeWrapper *>(effect->object);
+ if(sampleFrames != 0 && that != nullptr)
+ {
+ that->BuildProcessBuffer(ProcessMsg::processReplacing, effect->numInputs, effect->numOutputs, inputs, outputs, sampleFrames);
+ }
+}
+
+
+void VSTCALLBACK BridgeWrapper::ProcessDoubleReplacing(AEffect *effect, double **inputs, double **outputs, int32 sampleFrames)
+{
+ BridgeWrapper *that = static_cast<BridgeWrapper *>(effect->object);
+ if(sampleFrames != 0 && that != nullptr)
+ {
+ that->BuildProcessBuffer(ProcessMsg::processDoubleReplacing, effect->numInputs, effect->numOutputs, inputs, outputs, sampleFrames);
+ }
+}
+
+
+template <typename buf_t>
+void BridgeWrapper::BuildProcessBuffer(ProcessMsg::ProcessType type, int32 numInputs, int32 numOutputs, buf_t **inputs, buf_t **outputs, int32 sampleFrames)
+{
+ if(!m_processMem.Good())
+ {
+ MPT_ASSERT_NOTREACHED();
+ return;
+ }
+
+ ProcessMsg *processMsg = m_processMem.Data<ProcessMsg>();
+ new(processMsg) ProcessMsg{type, numInputs, numOutputs, sampleFrames};
+
+ // Anticipate that many plugins will query the play position in a process call and send it along the process call
+ // to save some valuable inter-process calls.
+ m_sharedMem->timeInfo = *FromIntPtr<VstTimeInfo>(CVstPlugin::MasterCallBack(&m_sharedMem->effect, audioMasterGetTime, 0, kVstNanosValid | kVstPpqPosValid | kVstTempoValid | kVstBarsValid | kVstCyclePosValid | kVstTimeSigValid | kVstSmpteValid | kVstClockValid, nullptr, 0.0f));
+
+ buf_t *ptr = reinterpret_cast<buf_t *>(processMsg + 1);
+ for(int32 i = 0; i < numInputs; i++)
+ {
+ std::memcpy(ptr, inputs[i], sampleFrames * sizeof(buf_t));
+ ptr += sampleFrames;
+ }
+ // Theoretically, we should memcpy() instead of memset() here in process(), but OpenMPT always clears the output buffer before processing so it doesn't matter.
+ memset(ptr, 0, numOutputs * sampleFrames * sizeof(buf_t));
+
+ // In case we called Process() from the GUI thread (panic button or song stop => CSoundFile::SuspendPlugins)
+ m_isAudioThread = true;
+
+ m_sigProcessAudio.Send();
+ const HANDLE objects[] = {m_sigProcessAudio.confirm, m_sigToHostAudio.send, m_otherProcess};
+ DWORD result;
+ do
+ {
+ result = WaitForMultipleObjects(mpt::saturate_cast<DWORD>(std::size(objects)), objects, FALSE, INFINITE);
+ if(result == WAIT_OBJECT_0 + 1)
+ {
+ ParseNextMessage(m_sharedMem->audioThreadToHostMsgID);
+ m_sigToHostAudio.Confirm();
+ }
+ } while(result != WAIT_OBJECT_0 && result != WAIT_OBJECT_0 + 2 && result != WAIT_FAILED);
+
+ m_isAudioThread = false;
+
+ for(int32 i = 0; i < numOutputs; i++)
+ {
+ //std::memcpy(outputs[i], ptr, sampleFrames * sizeof(buf_t));
+ outputs[i] = ptr; // Exactly what you don't want plugins to do usually (bend your output pointers)... muahahaha!
+ ptr += sampleFrames;
+ }
+
+ // Did we receive any audioMasterProcessEvents data?
+ if(auto *events = m_eventMem.Data<int32>(); events != nullptr && *events != 0)
+ {
+ std::vector<char> eventCache;
+ TranslateBridgeToVstEvents(eventCache, events);
+ *events = 0;
+ CVstPlugin::MasterCallBack(&m_sharedMem->effect, audioMasterProcessEvents, 0, 0, eventCache.data(), 0.0f);
+ }
+}
+
+
+LRESULT CALLBACK BridgeWrapper::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if(hwnd == m_communicationWindow && wParam < m_plugins.size())
+ {
+ auto *that = static_cast<BridgeWrapper *>(m_plugins[wParam]);
+ if(that != nullptr && uMsg == WM_BRIDGE_MESSAGE_TO_HOST)
+ {
+ that->ParseNextMessage(static_cast<int>(lParam));
+ return WM_BRIDGE_SUCCESS;
+ }
+ }
+
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+#endif // MPT_WITH_VST
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.h b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.h
new file mode 100644
index 00000000..47df012f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/BridgeWrapper.h
@@ -0,0 +1,237 @@
+/*
+ * BridgeWrapper.h
+ * ---------------
+ * Purpose: VST plugin bridge wrapper (host side)
+ * 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"
+
+#ifdef MPT_WITH_VST
+
+#include "BridgeCommon.h"
+#include "../common/ComponentManager.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+struct VSTPluginLib;
+
+enum PluginArch : int
+{
+ PluginArch_unknown = 0,
+ PluginArch_x86 = 32,
+ PluginArch_amd64 = 64,
+ PluginArch_arm = 128 + 32,
+ PluginArch_arm64 = 128 + 64,
+};
+
+std::size_t GetPluginArchPointerSize(PluginArch arch);
+
+enum class Generation
+{
+ Legacy,
+ Modern,
+};
+
+class ComponentPluginBridge
+ : public ComponentBase
+{
+public:
+ enum Availability
+ {
+ AvailabilityUnknown = 0,
+ AvailabilityOK = 1,
+ AvailabilityMissing = -1,
+ AvailabilityWrongVersion = -2,
+ };
+private:
+ const PluginArch arch;
+ const Generation generation;
+ mpt::PathString exeName;
+ Availability availability = AvailabilityUnknown;
+protected:
+ ComponentPluginBridge(PluginArch arch, Generation generation);
+protected:
+ bool DoInitialize() override;
+public:
+ Availability GetAvailability() const { return availability; }
+ mpt::PathString GetFileName() const { return exeName; }
+};
+
+class ComponentPluginBridge_x86
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridge_x86, "PluginBridge-x86")
+public:
+ ComponentPluginBridge_x86() : ComponentPluginBridge(PluginArch_x86, Generation::Modern) { }
+};
+
+class ComponentPluginBridgeLegacy_x86
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridgeLegacy_x86, "PluginBridgeLegacy-x86")
+public:
+ ComponentPluginBridgeLegacy_x86() : ComponentPluginBridge(PluginArch_x86, Generation::Legacy) { }
+};
+
+class ComponentPluginBridge_amd64
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridge_amd64, "PluginBridge-amd64")
+public:
+ ComponentPluginBridge_amd64() : ComponentPluginBridge(PluginArch_amd64, Generation::Modern) { }
+};
+
+class ComponentPluginBridgeLegacy_amd64
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridgeLegacy_amd64, "PluginBridgeLegacy-amd64")
+public:
+ ComponentPluginBridgeLegacy_amd64() : ComponentPluginBridge(PluginArch_amd64, Generation::Legacy) { }
+};
+
+#if defined(MPT_WITH_WINDOWS10)
+
+class ComponentPluginBridge_arm
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridge_arm, "PluginBridge-arm")
+public:
+ ComponentPluginBridge_arm() : ComponentPluginBridge(PluginArch_arm, Generation::Modern) { }
+};
+
+class ComponentPluginBridgeLegacy_arm
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridgeLegacy_arm, "PluginBridgeLegacy-arm")
+public:
+ ComponentPluginBridgeLegacy_arm() : ComponentPluginBridge(PluginArch_arm, Generation::Legacy) { }
+};
+
+class ComponentPluginBridge_arm64
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridge_arm64, "PluginBridge-arm64")
+public:
+ ComponentPluginBridge_arm64() : ComponentPluginBridge(PluginArch_arm64, Generation::Modern) { }
+};
+
+class ComponentPluginBridgeLegacy_arm64
+ : public ComponentPluginBridge
+{
+ MPT_DECLARE_COMPONENT_MEMBERS(ComponentPluginBridgeLegacy_arm64, "PluginBridgeLegacy-arm64")
+public:
+ ComponentPluginBridgeLegacy_arm64() : ComponentPluginBridge(PluginArch_arm64, Generation::Legacy) { }
+};
+
+
+
+#endif // MPT_WITH_WINDOWS10
+
+class BridgeWrapper : private BridgeCommon
+{
+protected:
+ Event m_sigAutomation;
+ MappedMemory m_oldProcessMem;
+
+ // Helper struct for keeping track of auxiliary shared memory
+ struct AuxMem
+ {
+ std::atomic<bool> used = false;
+ std::atomic<uint32> size = 0;
+ MappedMemory memory;
+ wchar_t name[64];
+ };
+ AuxMem m_auxMems[MSG_STACK_SIZE];
+
+ std::vector<char> m_cachedProgNames;
+ std::vector<ParameterInfo> m_cachedParamInfo;
+ std::vector<float> m_cachedParamValues;
+ int32 m_cachedProgNameStart = 0, m_cachedParamInfoStart = 0;
+
+ bool m_isSettingProgram = false;
+
+ Vst::ERect m_editRect;
+ Vst::VstSpeakerArrangement m_speakers[2];
+
+ ComponentHandle<ComponentPluginBridge_x86> pluginBridge_x86;
+ ComponentHandle<ComponentPluginBridgeLegacy_x86> pluginBridgeLegacy_x86;
+ ComponentHandle<ComponentPluginBridge_amd64> pluginBridge_amd64;
+ ComponentHandle<ComponentPluginBridgeLegacy_amd64> pluginBridgeLegacy_amd64;
+#if defined(MPT_WITH_WINDOWS10)
+ ComponentHandle<ComponentPluginBridge_arm> pluginBridge_arm;
+ ComponentHandle<ComponentPluginBridgeLegacy_arm> pluginBridgeLegacy_arm;
+ ComponentHandle<ComponentPluginBridge_arm64> pluginBridge_arm64;
+ ComponentHandle<ComponentPluginBridgeLegacy_arm64> pluginBridgeLegacy_arm64;
+#endif // MPT_WITH_WINDOWS10
+
+ Generation m_Generation = Generation::Modern;
+
+public:
+
+ // Generic bridge exception
+ class BridgeException : public std::exception
+ {
+ public:
+ BridgeException(const char *str) : std::exception(str) { }
+ BridgeException() { }
+ };
+ class BridgeNotFoundException : public BridgeException { };
+
+ // Exception from bridge process
+ class BridgeRemoteException
+ {
+ protected:
+ wchar_t *str;
+ public:
+ BridgeRemoteException(const wchar_t *str_) : str(_wcsdup(str_)) { }
+ BridgeRemoteException(const BridgeRemoteException &) = delete;
+ BridgeRemoteException & operator=(const BridgeRemoteException &) = delete;
+ ~BridgeRemoteException() { free(str); }
+ const wchar_t *what() const { return str; }
+ };
+
+public:
+ static PluginArch GetNativePluginBinaryType();
+ static PluginArch GetPluginBinaryType(const mpt::PathString &pluginPath);
+ static bool IsPluginNative(const mpt::PathString &pluginPath) { return GetPluginBinaryType(pluginPath) == GetNativePluginBinaryType(); }
+ static uint64 GetFileVersion(const WCHAR *exePath);
+
+ static Vst::AEffect *Create(const VSTPluginLib &plugin, bool forceLegacy);
+
+protected:
+ BridgeWrapper();
+ ~BridgeWrapper();
+
+ bool Init(const mpt::PathString &pluginPath, Generation bridgeGeneration, BridgeWrapper *sharedInstace);
+
+ void ParseNextMessage(int msgID);
+ void DispatchToHost(DispatchMsg &msg);
+ bool SendToBridge(BridgeMessage &sendMsg);
+ void SendAutomationQueue();
+ AuxMem *GetAuxMemory(uint32 size);
+
+ static intptr_t VSTCALLBACK DispatchToPlugin(Vst::AEffect *effect, Vst::VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt);
+ intptr_t DispatchToPlugin(Vst::VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt);
+ static void VSTCALLBACK SetParameter(Vst::AEffect *effect, int32 index, float parameter);
+ void SetParameter(int32 index, float parameter);
+ static float VSTCALLBACK GetParameter(Vst::AEffect *effect, int32 index);
+ float GetParameter(int32 index);
+ static void VSTCALLBACK Process(Vst::AEffect *effect, float **inputs, float **outputs, int32 sampleFrames);
+ static void VSTCALLBACK ProcessReplacing(Vst::AEffect *effect, float **inputs, float **outputs, int32 sampleFrames);
+ static void VSTCALLBACK ProcessDoubleReplacing(Vst::AEffect *effect, double **inputs, double **outputs, int32 sampleFrames);
+
+ template<typename buf_t>
+ void BuildProcessBuffer(ProcessMsg::ProcessType type, int32 numInputs, int32 numOutputs, buf_t **inputs, buf_t **outputs, int32 sampleFrames);
+
+ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+};
+
+OPENMPT_NAMESPACE_END
+
+#endif // MPT_WITH_VST
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win10.manifest b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win10.manifest
new file mode 100644
index 00000000..7e52c389
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win10.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity
+ type="win32"
+ name="OpenMPT.PluginBridge"
+ version="1.0.0.0"
+ />
+ <description>OpenMPT PluginBridge</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win7.manifest b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win7.manifest
new file mode 100644
index 00000000..e7fb589d
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win7.manifest
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity
+ type="win32"
+ name="OpenMPT.PluginBridge"
+ version="1.0.0.0"
+ />
+ <description>OpenMPT PluginBridge</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win81.manifest b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win81.manifest
new file mode 100644
index 00000000..b7f2aed3
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge-win81.manifest
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity
+ type="win32"
+ name="OpenMPT.PluginBridge"
+ version="1.0.0.0"
+ />
+ <description>OpenMPT PluginBridge</description>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge.rc b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge.rc
new file mode 100644
index 00000000..dda3bea7
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/pluginBridge/PluginBridge.rc
@@ -0,0 +1,109 @@
+// Microsoft Visual C++ generated resource script.
+//
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// German (Germany) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+
+#include "../common/versionNumber.h"
+
+#define VER_HELPER_STRINGIZE(x) #x
+#define VER_STRINGIZE(x) VER_HELPER_STRINGIZE(x)
+
+#define VER_FILEVERSION VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR
+#define VER_FILEVERSION_DOT VER_MAJORMAJOR.VER_MAJOR.VER_MINOR.VER_MINORMINOR
+#define VER_FILEVERSION_STR VER_STRINGIZE(VER_FILEVERSION_DOT)
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.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 VER_FILEVERSION
+ PRODUCTVERSION VER_FILEVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName", "OpenMPT"
+ VALUE "FileDescription", "OpenMPT Plugin Bridge"
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ VALUE "InternalName", "PluginBridge.exe"
+ VALUE "LegalCopyright", "Copyright © 2013-2022 OpenMPT Project Developers and Contributors"
+ VALUE "OriginalFilename", "PluginBridge.exe"
+ VALUE "ProductName", "OpenMPT"
+ VALUE "ProductVersion", VER_FILEVERSION_STR
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // German (Germany) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+