Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
|
@ -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
|
1318
Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.cpp
Normal file
1318
Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.cpp
Normal file
File diff suppressed because it is too large
Load diff
104
Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.h
Normal file
104
Src/external_dependencies/openmpt-trunk/pluginBridge/Bridge.h
Normal 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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue