I would like to create a simple FX VST3 host in Delphi, to pass samples to a plugin and get the effected audio.
The code below works until the point where the editor window should appear, but it crashes with a $18 address AV error in the plugin DLL, so seems there is a missing object.
What is missing for the editor window to appear - and generally how to use an FX VST3 plugin?
type
THostApplication = class (TInterfacedObject, IHostApplication, IPlugFrame)
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
// Gets host application name.
function GetName(name: PString128): TResult; stdcall;
// Create host object (e.g. Vst::IMessage).
function CreateInstance(cid: TUID; iid: TUID; var obj: pointer): TResult; stdcall;
// Called to inform the host about the resize of a given view.
function ResizeView(view: IPlugView; newSize: PViewRect): TResult; stdcall;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
DLLHandle: THandle;
PluginInit: TPluginInitExit;
PluginExit: TPluginInitExit;
PluginFactoryCall: TGetPluginFactory;
PluginFactoryCallResult: Pointer;
PluginFactory: IPluginFactory;
FactoryInfo: TPFactoryInfo;
ClassInfo: TPClassInfo;
Component: IComponent;
PluginInstance: Pointer;
AudioProcessor: IAudioProcessor;
Error: Cardinal;
HostApplication: THostApplication;
EditController: IEditController;
cp_comp: IConnectionPoint;
cp_edit: IConnectionPoint;
PlugView: IPlugView;
PanelHandle: HWND;
ProcessSetup: TProcessSetup;
SpeakerArrangement: TSpeakerArrangement;
begin
Set8087CW($133F);
DLLHandle := LoadLibrary(PChar('ReLife.vst3'));
PluginInit := GetProcAddress(DLLHandle, PAnsiChar('InitDLL'));
PluginExit := GetProcAddress(DLLHandle, PAnsiChar('ExitDLL'));
if Assigned(PluginInit) then begin
PluginInit;
end;
PluginFactoryCall := GetProcAddress(DLLHandle, PAnsiChar('GetPluginFactory'));
PluginFactoryCallResult := PluginFactoryCall;
PluginFactory := IPluginFactory(PluginFactoryCallResult);
PluginFactory.GetFactoryInfo(FactoryInfo);
PluginFactory.GetClassInfo(0, ClassInfo);
PluginFactory.CreateInstance(PANSIChar(@ClassInfo.cid), PANSIChar(@UID_IComponent), PluginInstance);
Component := IComponent(PluginInstance);
Component.SetIoMode(kSimple);
HostApplication := THostApplication.Create;
Error := Component.Initialize(HostApplication);
PluginFactory.CreateInstance(PANSIChar(@ClassInfo.cid), PANSIChar(@UID_IAudioProcessor), PluginInstance);
AudioProcessor := IAudioProcessor(PluginInstance);
ProcessSetup.processMode := kRealtime;
ProcessSetup.symbolicSampleSize := kSample32;
ProcessSetup.maxSamplesPerBlock := 1024;
ProcessSetup.sampleRate := 44100;
SpeakerArrangement := 3;
AudioProcessor.SetBusArrangements(@SpeakerArrangement, 2, @SpeakerArrangement, 2);
AudioProcessor.SetupProcessing(ProcessSetup);
AudioProcessor.SetProcessing(1);
PluginFactory.CreateInstance(PANSIChar(@ClassInfo.cid), PANSIChar(@UID_IEditController), PluginInstance);
EditController := IEditController(PluginInstance);
Error := EditController.Initialize(HostApplication);
//Component.QueryInterface(UID_IConnectionPoint, cp_comp);
//EditController.QueryInterface(UID_IConnectionPoint, cp_edit);
//cp_comp.Connect(cp_edit);
//cp_edit.Connect(cp_comp);
PlugView := IPlugView(EditController.CreateView(PANSIChar(kEditor)));
PlugView.SetFrame(HostApplication);
PanelHandle := Panel1.Handle;
PlugView.Attached(Pointer(PanelHandle), kPlatformTypeHWND);
end;
function THostApplication.CreateInstance(cid, iid: TUID; var obj: pointer): TResult;
begin
//* Don't know what to put here?
Result := 0;
end;
function THostApplication.GetName(name: PString128): TResult;
begin
Result := 0;
end;
function THostApplication.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if (IID = UID_IPlugFrame)
OR (IID = UID_FUnknown)
then begin
Pointer(Obj) := Self;
Result := kResultTrue;
end else begin
Result := inherited;
end;
end;
function THostApplication.ResizeView(view: IPlugView; newSize: PViewRect): TResult;
begin
//* TODO
Result := 0;
end;
EDIT: Found this code:
// connect the 2 components
Vst::IConnectionPoint* iConnectionPointComponent = nullPtr;
Vst::IConnectionPoint* iConnectionPointController = nullPtr;
processorComponent->queryInterface (Vst::IConnectionPoint::iid, (void**)&iConnectionPointComponent);
editController->queryInterface (Vst::IConnectionPoint::iid, (void**)&iConnectionPointController);
if (iConnectionPointComponent && iConnectionPointController)
{
iConnectionPointComponent->connect (iConnectionPointController);
iConnectionPointController->connect (iConnectionPointComponent);
}
// synchronize controller to component by using setComponentState
MemoryStream stream; // defined in "public.sdk/source/common/memorystream.h"
stream.setByteOrder (kLittleEndian);
if (processorComponent->getState (&stream) == kResultTrue)
{
stream.rewind ();
editController->setComponentState (&stream);
}
// now processorComponent and editController parts are connected and synchronized...:-)
}
But when I try this in Delphi it returns a 'kNoInterface' $80004002 'E_NOINTERFACE' in 'HRes'.
HRes := Component.QueryInterface(UID_IConnectionPoint, cp_comp);
Showmessage(IntToStr(HRes));
HRes := EditController.QueryInterface(UID_IConnectionPoint, cp_edit);
Showmessage(IntToStr(HRes));
Res := cp_comp.Connect(cp_edit);
Showmessage(IntToStr(Res));
cp_edit.Connect(cp_comp);
So I'm pretty much stuck now. Anybody any ideas?