286 lines
6.9 KiB
C++
286 lines
6.9 KiB
C++
#include "VST2PluginHost.h"
|
|
|
|
#include "Singleton/PortAudioAPI.h"
|
|
#include "Mixer/MixerTrack.h"
|
|
#include "HAL/PlatformProcess.h"
|
|
// #include "Widget/WindowManager.h"
|
|
|
|
DEFINE_LOG_CATEGORY(LogVST2PluginHost);
|
|
|
|
static TMap<FString, TSharedPtr<FVST2PluginHandle>> PluginHandles;
|
|
|
|
TSharedPtr<FVST2PluginHandle> TryLoadHandle(FString Path)
|
|
{
|
|
if (TSharedPtr<FVST2PluginHandle>* Handle = PluginHandles.Find(Path))
|
|
return *Handle;
|
|
|
|
void* DllHandle = FPlatformProcess::GetDllHandle(*Path);
|
|
if (!DllHandle)
|
|
return nullptr;
|
|
TSharedPtr<FVST2PluginHandle>& NewHandle = PluginHandles.Add(Path, MakeShared<FVST2PluginHandle>());
|
|
NewHandle->Path = Path;
|
|
NewHandle->Handle = DllHandle;
|
|
return NewHandle;
|
|
}
|
|
|
|
typedef AEffect* (*VSTPluginEntryProc)(audioMasterCallback AudioMaster);
|
|
|
|
TMap<FName, bool> CanDoMap =
|
|
{
|
|
{"sendVstEvents", true},
|
|
{"sendVstMidiEvent", true},
|
|
{"sendVstTimeInfo", true},
|
|
{"receiveVstEvents", false},
|
|
{"receiveVstMidiEvent", false},
|
|
{"receiveVstTimeInfo", false},
|
|
{"offline", false},
|
|
{"reportConnectionChanges", false},
|
|
{"acceptIOChanges", true},
|
|
{"sizeWindow", true},
|
|
{"asyncProcessing", true},
|
|
{"supplyIdle", true},
|
|
{"supportShell", false},
|
|
{"openFileSelector", false},
|
|
{"editFile", false},
|
|
{"closeFileSelector", false},
|
|
{"NIMKPIVendorSpecificCallbacks", false},
|
|
};
|
|
|
|
bool VstHostCanDo(const char* CanDo)
|
|
{
|
|
const FName CanDoName = FName(UTF8_TO_TCHAR(CanDo));
|
|
const bool* Find = CanDoMap.Find(CanDoName);
|
|
check(Find)
|
|
if (Find)
|
|
return *Find;
|
|
return false;
|
|
}
|
|
|
|
VstIntPtr MasterCallback(AEffect* Effect, VstInt32 OpCode, VstInt32 Index, VstIntPtr Value, void* Ptr, float Opt)
|
|
{
|
|
switch (OpCode)
|
|
{
|
|
case audioMasterAutomate:
|
|
return 1;
|
|
case audioMasterVersion:
|
|
return kVstVersion; // 返回插件版本号
|
|
case audioMasterWantMidi:
|
|
return 1;
|
|
case audioMasterGetSampleRate:
|
|
return FPortAudioAPI::Get().SampleRate; // 返回采样率
|
|
case audioMasterGetCurrentProcessLevel:
|
|
return kVstProcessLevelRealtime; // 返回当前处理级别
|
|
case audioMasterGetBlockSize:
|
|
return FPortAudioAPI::Get().BlockSize; // 返回块大小
|
|
case audioMasterSizeWindow:
|
|
{
|
|
// 设置插件窗口大小
|
|
// FVST2PluginHost* Host = static_cast<FVST2PluginHost*>(Effect->user);
|
|
// if (const TSharedPtr<SWindow> Window = FWindowManager::Get().FindPluginEditor(Host))
|
|
// Window->Resize(FVector2D(Index, Value));
|
|
return 1;
|
|
}
|
|
case audioMasterGetTime:
|
|
return (VstIntPtr)&VST2::VSTTimeInfo;
|
|
case audioMasterIdle:
|
|
UE_LOG(LogTemp, Log, TEXT("Plugin Idle"));
|
|
return 1;
|
|
case audioMasterNeedIdle:
|
|
UE_LOG(LogTemp, Log, TEXT("Plugin Need Idle"));
|
|
return 1;
|
|
case audioMasterGetVendorString:
|
|
{
|
|
const char* Ptr1 = (const char*)Ptr;
|
|
Ptr1 = "Arona";
|
|
return 1;
|
|
}
|
|
case audioMasterGetProductString:
|
|
{
|
|
const char* Ptr1 = (const char*)Ptr;
|
|
Ptr1 = "Arona";
|
|
return 1;
|
|
}
|
|
case audioMasterGetVendorVersion:
|
|
return 1000;
|
|
case audioMasterCanDo:
|
|
{
|
|
const char* Ptr1 = (const char*)Ptr;
|
|
return VstHostCanDo(Ptr1) ? 1 : 0;
|
|
}
|
|
case audioMasterUpdateDisplay:
|
|
return 1;
|
|
case audioMasterBeginEdit:
|
|
{
|
|
UE_LOG(LogTemp, Log, TEXT("Want Begin Edit"));
|
|
return 1;
|
|
}
|
|
case audioMasterEndEdit:
|
|
{
|
|
UE_LOG(LogTemp, Log, TEXT("Want End Edit"));
|
|
return 1;
|
|
}
|
|
case audioMasterVendorSpecific:
|
|
return 0;
|
|
case audioMasterProcessEvents:
|
|
// TODO
|
|
{
|
|
VstEvents* Events = (VstEvents*)Ptr;
|
|
Events->events[0]->type;
|
|
FVST2PluginHost* Host = static_cast<FVST2PluginHost*>(Effect->user);
|
|
|
|
}
|
|
return 1;
|
|
default:
|
|
ensureMsgf(0, TEXT("No Implement OpCode: %d"), OpCode);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
FVST2PluginHandle::~FVST2PluginHandle()
|
|
{
|
|
FPlatformProcess::FreeDllHandle(Handle);
|
|
}
|
|
|
|
FVST2PluginHost::FVST2PluginHost()
|
|
{
|
|
Handle = nullptr;
|
|
Effect = nullptr;
|
|
}
|
|
|
|
FVST2PluginHost::~FVST2PluginHost()
|
|
{
|
|
UE_LOG(LogVST2PluginHost, Log, TEXT("VST2Plugin Destroyed: %s"), *Name);
|
|
|
|
if (Effect)
|
|
Dispatch(effClose);
|
|
|
|
Effect = nullptr;
|
|
Handle.Reset();
|
|
MidiEventsToSend.freeEvents();
|
|
}
|
|
|
|
bool FVST2PluginHost::Load(const FString& Path)
|
|
{
|
|
Handle = TryLoadHandle(Path);
|
|
if (!Handle.IsValid())
|
|
{
|
|
UE_LOG(LogVST2PluginHost, Error, TEXT("加载插件失败 %s: 无法加载库文件"), *Path);
|
|
return false;
|
|
}
|
|
|
|
VSTPluginEntryProc PluginEntryProc = static_cast<VSTPluginEntryProc>(FPlatformProcess::GetDllExport(Handle->Handle, TEXT("VSTPluginMain")));
|
|
if (!PluginEntryProc)
|
|
{
|
|
UE_LOG(LogVST2PluginHost, Error, TEXT("加载插件失败 %s: 没有找到函数入口"), *Path);
|
|
return false;
|
|
}
|
|
|
|
Effect = PluginEntryProc(&MasterCallback);
|
|
check(Effect->magic == kEffectMagic)
|
|
|
|
Effect->user = this;
|
|
|
|
char Buf[256];
|
|
Dispatch(effGetEffectName, 0, 0, Buf);
|
|
Name = UTF8_TO_TCHAR(Buf);
|
|
|
|
Dispatch(effGetVendorString, 0, 0, Buf);
|
|
Vendor = FName(UTF8_TO_TCHAR(Buf));
|
|
|
|
|
|
UpdateSampleRate(FPortAudioAPI::Get().SampleRate);
|
|
UpdateBlockSize(FPortAudioAPI::Get().BlockSize);
|
|
|
|
ChannelInterface = new FChannelInterface(Effect->numInputs, Effect->numOutputs);
|
|
|
|
|
|
Dispatch(effOpen);
|
|
SetEnabled(true);
|
|
return true;
|
|
}
|
|
|
|
void FVST2PluginHost::SetEnabled(bool bEnabled)
|
|
{
|
|
EnableState = bEnabled;
|
|
Dispatch(effMainsChanged, 0, bEnabled);
|
|
}
|
|
|
|
bool FVST2PluginHost::IsEnabled()
|
|
{
|
|
return EnableState;
|
|
}
|
|
|
|
void FVST2PluginHost::UpdateBlockSize(int32 BlockSize)
|
|
{
|
|
Dispatch(effSetBlockSize, 0, BlockSize);
|
|
}
|
|
|
|
void FVST2PluginHost::UpdateSampleRate(float SampleRate)
|
|
{
|
|
Dispatch(effSetSampleRate, 0, 0, nullptr, SampleRate);
|
|
}
|
|
|
|
void FVST2PluginHost::Process(int32 NumSamples)
|
|
{
|
|
MidiEventsToSend.clear();
|
|
MidiEventsToSend.ensureSize(1);
|
|
|
|
for (const auto metadata : IncomingMidi)
|
|
MidiEventsToSend.addEvent(metadata->message.getRawData(), metadata->message.getRawDataSize(), FMath::Clamp(metadata->message.getTimeStamp(), 0, NumSamples - 1));
|
|
|
|
Dispatch(effProcessEvents, 0, 0, MidiEventsToSend.events);
|
|
IncomingMidi.clear();
|
|
|
|
Effect->processReplacing(Effect, ChannelInterface->GetInputHeader(), ChannelInterface->GetOutputHeader(), NumSamples);
|
|
}
|
|
|
|
void FVST2PluginHost::UpdateChannelNodeName()
|
|
{
|
|
VstPinProperties PinProperties;
|
|
for (int i = 0; i < Effect->numInputs; ++i)
|
|
{
|
|
if (Dispatch(effGetInputProperties, i, 0, &PinProperties) == 1)
|
|
{
|
|
ChannelInterface->SetInputChannelNodeName(i / 2, i, UTF8_TO_TCHAR(PinProperties.label));
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < Effect->numOutputs; ++i)
|
|
{
|
|
if (Dispatch(effGetOutputProperties, i, 0, &PinProperties) == 1)
|
|
{
|
|
ChannelInterface->SetOutputChannelNodeName(i / 2, i, UTF8_TO_TCHAR(PinProperties.label));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FVST2PluginHost::OpenEditor(void* WindowHandle)
|
|
{
|
|
if (!HasEditor())
|
|
return;
|
|
Dispatch(effEditOpen, 0, 0, WindowHandle);
|
|
}
|
|
|
|
void FVST2PluginHost::CloseEditor()
|
|
{
|
|
Dispatch(effEditClose);
|
|
}
|
|
|
|
void FVST2PluginHost::IdleEditor()
|
|
{
|
|
Dispatch(effEditIdle);
|
|
}
|
|
|
|
bool FVST2PluginHost::HasEditor()
|
|
{
|
|
return Effect->flags & effFlagsHasEditor;
|
|
}
|
|
|
|
FVector2D FVST2PluginHost::GetEditorSize()
|
|
{
|
|
ERect* EditorRect = nullptr;
|
|
Dispatch(effEditGetRect, 0, 0, &EditorRect);
|
|
return FVector2D(EditorRect->right - EditorRect->left, EditorRect->bottom - EditorRect->top);
|
|
}
|