Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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