Hi!
I would like to create a simple VST3 effect host in Delphi, just to show the editor, pass samples and get back the effected samples for start.
Examining the SDK tutorials and Googleing for examples, I managed to achieve the basic steps, but I am new to this, although I have a little experience with COM, and I run into a problem that I can’t get the IConnectionPoint interfaces. When I then try to open the editor it crashes with a $18 address AV in the plugin DLL.
Here’s the code that I have right now:
type
TPluginInitExit = function: LongBool; cdecl;
TGetPluginFactory = function: Pointer; stdcall;
type
THostApplication = class (TInterfacedObject, IHostApplication, IPlugFrame, IComponentHandler)
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;
// Transfer parameter editing to component via host.
// To be called before calling a performEdit (when mouse down for example).
function BeginEdit(tag: TParamID): TResult; stdcall;
// Called between beginEdit and endEdit to inform the handler that a given parameter has a new value.
function PerformEdit(tag: TParamID; valueNormalized: TParamValue): TResult; stdcall;
// To be called after calling a performEdit (when mouse up for example).
function EndEdit(tag: TParamID): TResult; stdcall;
// Instruct host to restart the component.
// - flags is a combination of RestartFlags
function RestartComponent(flags: int32): 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;
HostApplication: THostApplication;
EditController: IEditController;
cp_comp: IConnectionPoint;
cp_edit: IConnectionPoint;
PlugView: IPlugView;
ViewRect: TViewRect;
PanelHandle: HWND;
ProcessSetup: TProcessSetup;
SpeakerArrangement: TSpeakerArrangement;
Res: TResult;
BStream: TVST3Stream;
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(@ClassInfo.cid, @UID_IComponent, PluginInstance);
Component := IComponent(PluginInstance);
Component.SetIoMode(kSimple);
HostApplication := THostApplication.Create;
Res := Component.Initialize(HostApplication);
if Res <> 0 then begin
Showmessage('Error: Component.Initialize()');
end;
PluginFactory.CreateInstance(@ClassInfo.cid, @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(@ClassInfo.cid, @UID_IEditController, PluginInstance);
EditController := IEditController(PluginInstance);
EditController.SetComponentHandler(HostApplication);
Res := EditController.Initialize(HostApplication);
if Res <> 0 then begin
Showmessage('Error: EditController.Initialize()');
end;
Res := Component.QueryInterface(UID_IConnectionPoint, cp_comp);
if Res <> 0 then begin
if Res = kNoInterface then begin
Showmessage('kNoInterface'); //* ERROR HERE
end else begin
Showmessage(IntToStr(Res));
end;
end;
Res := EditController.QueryInterface(UID_IConnectionPoint, cp_edit);
if Res <> 0 then begin
if Res = kNoInterface then begin
Showmessage('kNoInterface'); //* ERROR HERE
end else begin
Showmessage(IntToStr(Res));
end;
end;
if Assigned(cp_comp)
AND Assigned(cp_edit)
then begin
Res := cp_comp.Connect(cp_edit);
if Res <> kResultOk then begin
Showmessage(IntToStr(Res));
end;
cp_edit.Connect(cp_comp);
if Res <> kResultOk then begin
Showmessage(IntToStr(Res));
end;
end;
// synchronize controller to component by using setComponentState
BStream := TVST3Stream.Create;
try
//BStream.setByteOrder(kLittleEndian);
if Component.GetState(BStream) = kResultTrue then begin
BStream.Seek(0, kIBSeekSet);
if EditController.SetComponentState(BStream) <> kResultTrue then begin
Showmessage('Error: EditController.SetComponentState()');
end;
end;
finally
FreeAndNil(BStream);
end;
Component.ActivateBus(kAudio, kInput, 0, 1);
Component.ActivateBus(kAudio, kOutput, 0, 1);
Component.SetActive(1);
PlugView := IPlugView(EditController.CreateView(PANSIChar(kEditor)));
PlugView.GetSize(@ViewRect);
PlugView.SetFrame(HostApplication);
PanelHandle := Panel1.Handle;
PlugView.Attached(Pointer(PanelHandle), kPlatformTypeHWND); //* CRASH HERE
end;
{ THostApplication }
function THostApplication.BeginEdit(tag: TParamID): TResult;
begin
Result := kNotImplemented;
end;
function THostApplication.CreateInstance(cid, iid: TUID; var obj: pointer): TResult;
begin
//* Don't know what to put here?
Result := 0;
end;
function THostApplication.EndEdit(tag: TParamID): TResult;
begin
Result := kNotImplemented;
end;
function THostApplication.GetName(name: PString128): TResult;
begin
name^ := HostName;
Result := kNotImplemented;
end;
function THostApplication.PerformEdit(tag: TParamID; valueNormalized: TParamValue): TResult;
begin
Result := kNotImplemented;
end;
function THostApplication.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
//* Not sure if this is needed at all
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;
function THostApplication.RestartComponent(flags: int32): TResult;
begin
Result := kNotImplemented;
end;
Thank you!