Wrapping VSTGUI for Dummies
Wrapping VSTGUI for Dummies
- Subject: Wrapping VSTGUI for Dummies
- From: Art Gillespie <email@hidden>
- Date: Thu, 2 Oct 2003 16:54:44 -0400
Peoples,
After spending the better part of the day doing some pro-bono work
trying to help out yet another dev with their Emagic VST->AU wrapper
woes, I came to the following conclusion:
The Emagic VST->AU wrapper is useless.
Below is a modified copy of VST2AUView.cpp with my comments and changes
(generally preceded with AG:). I tried to simply comment out the
original stuff that I changed, so you can compare and contrast. I also
tried to edit my original comments for content and political
sensitivity, but may have missed a few. :)
Hope this helps somebody out.
Best,
Art
>>0xBA
#include "VST2AUView.h"
#include "AEffEditor.hpp"
#ifndef __AudioEffect__
#include "AudioEffect.hpp"
#endif
//
------------------------------------------------------------------------
-----
// Class CVST2AUView
//
------------------------------------------------------------------------
-----
// Component entry point
-------------------------------------------------------
COMPONENT_ENTRY(CVST2AUView);
// -> V1.1: Mechanism for CodeWarrior symbol export
#ifdef __MWERKS__
#pragma export list CVST2AUViewEntry
#endif
// <- V1.1
//
------------------------------------------------------------------------
-----
CVST2AUView::CVST2AUView(AudioUnitCarbonView tView):
AUCarbonViewBase(tView),
m_pcVSTGUI(NULL),
m_u32Left(0),
m_u32Top(0),
m_u32Bottom(0),
m_u32Right(0),
theBundle(NULL) //AG: since we need the bundleRef so often, let's
keep it around!!!
{}
//--------------------------
// Special IdleProc o handle idle events of VSTGUI etc
pascal void IdleProc(EventLoopTimerRef tTimer, void *pUser)
{
if(pUser){
CVST2AUView *pcThis = (CVST2AUView *)(pUser);
GrafPtr tPort;
EventRecord sEvent;
GetPort(&tPort);
SetPortWindowPort(pcThis->m_tWindow);
sEvent.what = nullEvent;
//AG: SetOrigin is totally gay
//SetOrigin(pcThis->m_u32Left, -pcThis->m_u32Top);
pcThis->pcFX->dispatcher(effEditIdle, 0, 0, 0, 0.0f);
//AG: ibid
//SetOrigin(0, 0);
SetPort(tPort);
}
}
//
------------------------------------------------------------------------
-----
CVST2AUView::~CVST2AUView(void)
{
//idle proc and timer remove
if(m_tTimer)
RemoveEventLoopTimer(m_tTimer);
if(m_uppIdleProc)
DisposeEventLoopTimerUPP(m_uppIdleProc);
m_tTimer = NULL;
m_uppIdleProc = NULL;
//rest of all
//AG: use dispatcher instead of direct instance
//if (m_pcVSTGUI) m_pcVSTGUI->close();
pcFX->dispatcher(effEditClose, 0, 0, 0, 0.0f);
//delete m_pcVSTGUI;
//m_pcVSTGUI = NULL;
//pcFX->setEditor(NULL);
//AG: Since we kept our bundleref around, let's dispose of it
if (theBundle)
{
CFRelease(theBundle);
theBundle = NULL;
}
}
//
------------------------------------------------------------------------
-----
OSStatus CVST2AUView::CreateUI(Float32 rX, Float32 rY)
{
// -> V1.1: Put the unique bundle identifier here
// Get the bundle reference with the bundle ID
theBundle=CFBundleGetBundleWithIdentifier(CFSTR("com.pspaudioware.plugin
s.springverb"));
// <- V1.1
if (!theBundle)
{
//AG Why the fuck doesn't emagic check return values?
fprintf(stderr, "CFBundleGetBundleWithIdentifier Failed\n");
return kAudioUnitErr_FailedInitialization;
}
//AG We want to keep the BundleRef around
CFRetain(theBundle);
// Open the resources
const SInt16 s16OldRes=CurResFile();
const SInt16 s16Res=CFBundleOpenBundleResourceMap(theBundle);
UseResFile(s16Res);
// Get the calling Audio Unit
const AudioUnit tAU=GetEditAudioUnit();
// Get the this-ptr. This is very dirty!
AudioEffect* ptrEffect=NULL;
UInt32 u32Size=sizeof(AudioEffect*);
AudioUnitGetProperty(tAU, kVST2AudioUnitProperty_AudioEffectPtr,
kAudioUnitScope_Global, 0, (void*)(&ptrEffect), &u32Size);
pcFX = ptrEffect;
if (pcFX == NULL) //AG: check for errors!
return kAudioUnitErr_FailedInitialization;
// Create the VST GUI
//AG: The VST is supposed to manage it's own interface instance
// ... this wrapper *should* be using dispatcher opcodes to do all this
// if we create a new instance of the GUI and call SetEditor, with
most VSTs this will
// cause a memory leak as the plug-ins self-created UI pointer is
dereferenced.
// Broken. (see AEffect::setEditor)
//
//m_pcVSTGUI=new VST2AU_GUI_CLASS(pcFX);
//pcFX->setEditor(m_pcVSTGUI);
/**
* AG: The best-kept secret about VST's gui handling (or so it
would seem).
* The proper way to inform a VST UI (including VSTGUI) of offsets is
by getting
* a pointer to the editor's bounds rect and setting it. (Yes, this
is very, very
* bad OO). So here we use the dispatcher interface to get the
rect and offset it
* using the x, y offsets passed to CreateUI.
*
* It's up to the VST's UI to handle offsets, so all of the calls to
SetOrigin are
* unnecessary. VST's that use VSTGUI get this behavior
automatically. Unfortunately,
* it seems that many VSTGUI-based plugins don't pass the AEffEditor's
rect member as
* the first argument to CFrame's constructor (look at the source for
CFrame in vstgui.cpp
* and you'll see under the #elif MAC section that it sets up x, y
offsets for the drawing
* context).
*
* If you use this code and you're using VSTGUI and you find you're
still drawing without the
* appropriate offset, check to see that your passing the right
* rect to CFrame's constructor in your open() method.
*/
ERect* psRect=NULL;
pcFX->dispatcher(effEditGetRect, 0, 0, (void*)&psRect, 0.0f);
//m_pcVSTGUI->getRect(&psRect);
psRect->left+=UInt32(rX);
psRect->top+=UInt32(rY);
psRect->right+=UInt32(rX);
psRect->bottom+=UInt32(rY);
m_u32Left=psRect->left;
m_u32Top=psRect->top;
m_u32Bottom=psRect->bottom;
m_u32Right=psRect->right;
//AG: NO, NO, NO, NO... why do we need to do this? The dispatcher
interface doesn't
//provide for it, so why do we need it?
// See long-winded explanation of offsets for VST above
//m_pcVSTGUI->originX = m_u32Left;
//m_pcVSTGUI->originY = -m_u32Top;
//AG: SetOrigin is the devil
//SetOrigin(m_u32Left, -m_u32Top);
//AG: Use opcodes/dispatcher interface instead
//m_pcVSTGUI->open(WindowPtr(mCarbonWindow));
pcFX->dispatcher(effEditOpen, 0, 0, WindowPtr(mCarbonWindow), 0.0f);
mBottomRight.v=psRect->bottom;
mBottomRight.h=psRect->right;
GrafPtr tPort;
GetPort(&tPort);
SetPortWindowPort(mCarbonWindow);
//AG: NO
//SetOrigin(m_pcVSTGUI->originX, m_pcVSTGUI->originY);//m_u32Left,
-m_u32Top);
//AG: Huh? Why would we draw here? Most hosts won't even have
resized the window
//until we return from CreateUI
//ERect sRect;
//sRect.top=0;
//sRect.left=0;
//sRect.bottom=mBottomRight.v;
//sRect.right=mBottomRight.h;
//m_pcVSTGUI->draw(&sRect);
//
SetPort(tPort);
// Set the old resource file
UseResFile(s16OldRes);
/**
* AG: THE SMART WAY TO DO THIS IS TO USE CONTROL EVENTS ON THE
CARBON PANE
* I've commented out Emagic's original handler installer and put
in what
* I use. Emagic's version won't work consistently in hosts
other than logic (and not even
* then), causing
* mad mouse and draw bugs.
*
* // Add some events to the handler
* EventTypeSpec asWindowEvents[] =
* {
* { kEventClassMouse, kEventMouseDown },
* { kEventClassMouse, kEventMouseDragged },
*
* { kEventClassWindow, kEventWindowDrawContent },
* { kEventClassWindow, kEventWindowResizeCompleted },
* { kEventClassWindow, kEventWindowDragCompleted },
* { kEventClassWindow, kEventWindowUpdate }//,
*
* };
*
* WantEventTypes(GetWindowEventTarget(mCarbonWindow),
* GetEventTypeCount(asWindowEvents),
* asWindowEvents);
*/
/** AG: My Event handling scheme (ControlEvents work with hosts other
than Logic) **/
/* Register for control events on the Carbon Pane */
EventTypeSpec eventTypes[5];
eventTypes[0].eventClass = kEventClassControl;
eventTypes[0].eventKind = kEventControlDraw;
eventTypes[1].eventClass = kEventClassControl;
eventTypes[1].eventKind = kEventControlHit;
eventTypes[2].eventClass = kEventClassControl;
eventTypes[2].eventKind = kEventControlHitTest;
eventTypes[3].eventClass = kEventClassControl;
eventTypes[3].eventKind = kEventControlClick;
eventTypes[4].eventClass = kEventClassControl;
eventTypes[4].eventKind = kEventControlTrack;
WantEventTypes(GetControlEventTarget(mCarbonPane),
GetEventTypeCount(eventTypes),
eventTypes);
TXNInitTextension ( NULL, 0, 0 );
//Try to start event loop timer for idle() calls
m_tWindow = mCarbonWindow;
m_uppIdleProc = NewEventLoopTimerUPP(IdleProc);
OSStatus tErr = InstallEventLoopTimer(GetMainEventLoop(),
100. * kEventDurationMillisecond, 60 * kEventDurationMillisecond,
m_uppIdleProc,
this, &m_tTimer);
//AG: Should have been calling this
CFBundleCloseBundleResourceMap(theBundle, s16Res);
return noErr;
}
/**
AG: Commented out Emagic's HandleEvent.
//
------------------------------------------------------------------------
-----
bool CVST2AUView::HandleEvent(EventRef tEvent)
{
const UInt32 kTitleSize=16;
UInt32 iClass=GetEventClass(tEvent);
UInt32 iKind=GetEventKind(tEvent);
if (iClass==kEventClassWindow)
{
if (iKind==kEventWindowUpdate || iKind==kEventWindowDrawContent
|| iKind==kEventWindowResizeCompleted ||
iKind==kEventWindowDragCompleted)
{
// -> V1.1: Put the unique bundle identifier here
// Get the bundle reference with the bundle ID
CFBundleRef
tRef=CFBundleGetBundleWithIdentifier(CFSTR("com.pspaudioware.plugins.spr
ingverb"));
// <- V1.1
//AG: CHECK FOR FUCKING ERROR CODES!!!!
if (!tRef)
{
fprintf(stderr, "CFBundleGetBundleWithIdentifier Failed in
HandleEvent\n");
return false;
}
// Open the resources
const SInt16 s16OldRes=CurResFile();
const SInt16 s16Res=CFBundleOpenBundleResourceMap(tRef);
UseResFile(s16Res);
GrafPtr tPort;
GetPort(&tPort);
SetPortWindowPort(mCarbonWindow);
SetOrigin(m_u32Left, -m_u32Top);
ERect sRect;
sRect.top=0;
sRect.left=0;
sRect.bottom=mBottomRight.v;
sRect.right=mBottomRight.h;
m_pcVSTGUI->draw(&sRect);
// SetOrigin(0, 0);
SetPort(tPort);
// Set the old resource file
UseResFile(s16OldRes);
return true;
}
}
else if (iClass==kEventClassMouse)
{
if (iKind==kEventMouseDown || iKind==kEventMouseDragged)
{
fprintf(stderr, "Mouse Down\n");
HIPoint tPnt;
UInt32 u32Modifiers=0;
GetEventParameter(tEvent, kEventParamWindowMouseLocation,
typeHIPoint, NULL, sizeof(HIPoint), NULL, &tPnt);
GetEventParameter(tEvent, kEventParamKeyModifiers, typeUInt32, NULL,
sizeof(UInt32), NULL, &u32Modifiers);
tPnt.y-=kTitleSize;
if (tPnt.x>=m_u32Left && tPnt.x<m_u32Right && tPnt.y>=m_u32Top &&
tPnt.y<m_u32Bottom)
{
tPnt.y-=m_u32Top;
GrafPtr tPort;
GetPort(&tPort);
SetPortWindowPort(mCarbonWindow);
SetOrigin(m_u32Left, -m_u32Top);
m_pcVSTGUI->mouse(tPnt.x, tPnt.y);
// SetOrigin(0, 0);
SetPort(tPort);
return true;
}
}
}
return false;
}
*/
bool CVST2AUView::HandleEvent(EventRef inEvent)
{
UInt32 eventClass = GetEventClass(inEvent);
UInt32 eventKind = GetEventKind(inEvent);
switch (eventClass)
{
case kEventClassControl:
{
switch (eventKind)
{
case kEventControlDraw:
{
CGrafPtr save;
GetPort(&save);
SetPortWindowPort(mCarbonWindow);
Draw();
SetPort(save);
return true;
}
case kEventControlClick:
case kEventControlTrack:
{
CGrafPtr save;
GetPort(&save);
SetPortWindowPort(mCarbonWindow);
HIPoint mouseLocation_f;
memset(&mouseLocation_f, 0, sizeof(HIPoint));
GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint,
NULL,
sizeof(HIPoint), NULL, &mouseLocation_f);
long mouseX = (long) mouseLocation_f.x;
long mouseY = (long) mouseLocation_f.y;
MouseDown(mouseX, mouseY);
SetPort(save);
return true;
}
case kEventControlHitTest:
{
//AG: This is a really important event, if we don't return
something //sensible, the event manager won't send any more mouse
events
int where = 16;
SetEventParameter(inEvent, kEventParamControlPart,
typeControlPartCode,
sizeof(typeControlPartCode), &where);
return true;
}
default:
{
return AUCarbonViewBase::HandleEvent(inEvent);
}
}
}
}
return AUCarbonViewBase::HandleEvent(inEvent);
}
void CVST2AUView::MouseDown(long x, long y)
{
Rect controlBounds;
GetControlBounds(mCarbonPane, &controlBounds);
Rect globalBounds;
GetWindowBounds( GetControlOwner(mCarbonPane),
kWindowGlobalPortRgn, &globalBounds );
x -= globalBounds.left + controlBounds.left;
y -= globalBounds.top + controlBounds.top;
CGrafPtr port;
GetPort(&port);
SInt16 restmp = CurResFile();
SetPortWindowPort(mCarbonWindow);
SInt16 resNew = CFBundleOpenBundleResourceMap(theBundle);
//AG: robust implementations would chck resNew for validity
pcFX->dispatcher(effEditMouse, x, y, 0, 0.0f);
UseResFile(restmp);
CFBundleCloseBundleResourceMap(theBundle, resNew);
SetPort(port);
}
void CVST2AUView::Draw()
{
SInt16 restmp = CurResFile();
SInt16 resNew = CFBundleOpenBundleResourceMap(theBundle);
//AG: robust implementations would check resNew for validity
ERect* f;
pcFX->dispatcher(effEditGetRect, 0, 0, (void*)&f, 0.0f);
CGrafPtr port;
GetPort(&port);
SetPortWindowPort(mCarbonWindow);
pcFX->dispatcher(effEditDraw, 0, 0, (void*)f, 0.0f);
UseResFile(restmp);
CFBundleCloseBundleResourceMap(theBundle, resNew);
SetPort(port);
}
//
------------------------------------------------------------------------
-----
// End of file.
//
------------------------------------------------------------------------
-----
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.