1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-07 15:49:42 +02:00

Add some compatability for some Mac effects

Mac effects that use overlay windows should now work correctly.

And I made some minor changes to the settings dialog as suggested
by Gale.
This commit is contained in:
lllucius@gmail.com 2014-05-22 23:00:24 +00:00
parent 680896b1b9
commit 31d23fdf3f

View File

@ -765,7 +765,7 @@ void VSTEffectSettingsDialog::PopulateOrExchange(ShuttleGui & S)
S.StartHorizontalLay(wxALIGN_LEFT);
{
wxTextCtrl *t;
t = S.TieNumericTextBox(_("Buffer Size (8 to 1048576 samples)"),
t = S.TieNumericTextBox(_("&Buffer Size (8 to 1048576 samples):"),
wxT("/VST/BufferSize"),
wxT(""), 12);
t->SetMinSize(wxSize(100, -1));
@ -780,13 +780,13 @@ void VSTEffectSettingsDialog::PopulateOrExchange(ShuttleGui & S)
S.AddVariableText(wxString() +
_("As part of their processing, some VST effects must delay returning ") +
_("audio to Audacity. When not compensating for this delay, you will ") +
_("notice that bits of silence have been inserted into the audio. ") +
_("notice that small silences have been inserted into the audio. ") +
_("Enabling this setting will provide that compensation, but it may ") +
_("not work for all VST effects."))->Wrap(650);
S.StartHorizontalLay(wxALIGN_LEFT);
{
S.TieCheckBox(_("Enable compensation"), wxT("/VST/UseBufferDelay"), true);
S.TieCheckBox(_("Enable &compensation"), wxT("/VST/UseBufferDelay"), true);
}
S.EndHorizontalLay();
}
@ -797,8 +797,8 @@ void VSTEffectSettingsDialog::PopulateOrExchange(ShuttleGui & S)
S.AddVariableText(wxString() +
_("Most VST effects provide a graphical interface for setting the ") +
_("parameter values. However, a basic text only method is also ") +
_("available."))->Wrap(650);
S.TieCheckBox(_("Enable graphical interface"), wxT("/VST/GUI"), true);
_("available. Reopen the effect for this to take affect."))->Wrap(650);
S.TieCheckBox(_("Enable &graphical interface"), wxT("/VST/GUI"), true);
}
S.EndStatic();
@ -806,10 +806,10 @@ void VSTEffectSettingsDialog::PopulateOrExchange(ShuttleGui & S)
{
S.AddVariableText(wxString() +
_("To improve Audacity startup, a search for VST effects is performed ") +
_("once and relevent information is recorded. When you add VST effects ") +
_("once and relevant information is recorded. When you add VST effects ") +
_("to your system, you need to tell Audacity to rescan so the new ") +
_("information can be recorded."))->Wrap(650);
S.TieCheckBox(_("Rescan effects on next launch"), wxT("/VST/Rescan"), false);
S.TieCheckBox(_("&Rescan effects on next launch"), wxT("/VST/Rescan"), false);
}
S.EndStatic();
}
@ -893,8 +893,19 @@ class VSTEffectDialog:public wxDialog, XMLTagHandler
wxString mChunk;
#if defined(__WXMAC__)
EventHandlerUPP mHandlerUPP;
EventHandlerRef mHandlerRef;
static pascal OSStatus OverlayEventHandler(EventHandlerCallRef handler, EventRef event, void *data);
OSStatus OnOverlayEvent(EventHandlerCallRef handler, EventRef event);
static pascal OSStatus WindowEventHandler(EventHandlerCallRef handler, EventRef event, void *data);
OSStatus OnWindowEvent(EventHandlerCallRef handler, EventRef event);
WindowRef mOverlayRef;
EventHandlerUPP mOverlayEventHandlerUPP;
EventHandlerRef mOverlayEventHandlerRef;
bool mOverlayEventsBlocked;
WindowRef mWindowRef;
EventHandlerUPP mWindowEventHandlerUPP;
EventHandlerRef mWindowEventHandlerRef;
#endif
DECLARE_EVENT_TABLE()
@ -926,33 +937,318 @@ END_EVENT_TABLE()
#if defined(__WXMAC__)
// Event handler to capture the window close event
static const EventTypeSpec eventList[] =
// ----------------------------------------------------------------------------
// Most of the following is used to deal with VST effects that create an overlay
// window on top of ours. This is usually done because Cocoa is being used
// instead of Carbon.
//
// That works just fine...usually. But, we display the effect in a modal dialog
// box and, since that overlay window is just another window in the application,
// the modality of the dialog causes the overlay to be disabled and the user
// can't interact with the effect.
//
// Examples of these effects would be BlueCat's Freeware Pack and GRM Tools,
// though I'm certain there are other's out there. Anything JUCE based would
// affected...that's what GRM Tools uses.
//
// So, to work around the problem (without moving to Cocoa or wxWidgets 3.x),
// we install an event handler if the overlay is detected. This handler and
// the companion handler on our window use the kEventWindowGetClickModality
// event to tell the system that events can be passed to our window and the
// overlay window.
//
// In addition, there's some window state management that must be dealt with
// to keep our window from becoming unhightlighted when the floater is clicked.
// ----------------------------------------------------------------------------
// Events to be captured in the overlay window
static const EventTypeSpec OverlayEventList[] =
{
{kEventClassWindow, kEventWindowClose},
{ kEventClassWindow, kEventWindowGetClickModality },
{ kEventClassWindow, kEventWindowHandleActivate },
{ kEventClassWindow, kEventWindowActivated },
{ kEventClassMouse, kEventMouseDown },
{ kEventClassMouse, kEventMouseUp },
{ kEventClassMouse, kEventMouseMoved },
{ kEventClassMouse, kEventMouseDragged },
{ kEventClassMouse, kEventMouseEntered },
{ kEventClassMouse, kEventMouseExited },
{ kEventClassMouse, kEventMouseWheelMoved },
};
static pascal OSStatus EventHandler(EventHandlerCallRef handler, EventRef event, void *data)
// Overlay window event handler callback thunk
pascal OSStatus VSTEffectDialog::OverlayEventHandler(EventHandlerCallRef handler, EventRef event, void *data)
{
OSStatus result = eventNotHandledErr;
VSTEffectDialog *dlg = (VSTEffectDialog *)data;
if (GetEventClass(event) == kEventClassWindow && GetEventKind(event) == kEventWindowClose) {
dlg->RemoveHandler();
dlg->Close();
result = noErr;
}
return result;
return ((VSTEffectDialog *)data)->OnOverlayEvent(handler, event);
}
// Overlay window event handler
OSStatus VSTEffectDialog::OnOverlayEvent(EventHandlerCallRef handler, EventRef event)
{
// Get the current window in from of all the rest non-floaters.
WindowRef frontwin = FrontNonFloatingWindow();
// Get the target window of the event
WindowRef evtwin = 0;
GetEventParameter(event,
kEventParamDirectObject,
typeWindowRef,
NULL,
sizeof(evtwin),
NULL,
&evtwin);
#if defined(DEBUG_VST)
int cls = GetEventClass(event);
printf("WINDOW class %4.4s kind %d ewin %p owin %p mwin %p anf %p fnf %p\n",
&cls,
GetEventKind(event),
evtwin,
mOverlayRef,
mWindowRef,
ActiveNonFloatingWindow(),
frontwin);
#endif
// We must block mouse events because plugins still act on mouse
// movement and drag events, even if they are supposed to be disabled
// due to other modal dialogs (like when Load or Settings are clicked).
if (GetEventClass(event) == kEventClassMouse) {
if (mOverlayEventsBlocked) {
return noErr;
}
}
// Only kEventClassWindow event at this poine
switch (GetEventKind(event))
{
// The system is asking if the target of an upcoming event
// should be passed to the overlay window or not.
//
// We allow it when the overlay window or our window is the
// curret top window. Any other windows would mean that a
// modal dialog box has been open on top and we should block.
case kEventWindowGetClickModality:
{
HIModalClickResult res = kHIModalClickIsModal;
WindowRef ref = mWindowRef;
// Allow it to pass?
if (frontwin == mWindowRef | frontwin == mOverlayRef) {
res |= kHIModalClickAllowEvent; // | kHIModalClickRaiseWindow;
mOverlayEventsBlocked = false;
}
// No, block it
else {
res |= kHIModalClickAnnounce;
ref = frontwin;
mOverlayEventsBlocked = true;
}
// Get the kind of modal block from the modal window
WindowModality kind;
GetWindowModality(ref, &kind, NULL);
// Set the return parameters
SetEventParameter(event,
kEventParamWindowModality,
typeWindowRef,
sizeof(kind),
&kind);
SetEventParameter(event,
kEventParamModalWindow,
typeWindowRef,
sizeof(ref),
&ref);
SetEventParameter(event,
kEventParamModalClickResult,
typeModalClickResult,
sizeof(res),
&res);
return noErr;
}
break;
// Ignore the activate events to (sort of) keep our window as
// the active one
case kEventWindowHandleActivate:
case kEventWindowActivated:
{
return noErr;
}
break;
}
return eventNotHandledErr;
}
// Events to be captured in the our window
static const EventTypeSpec WindowEventList[] =
{
{ kEventClassWindow, kEventWindowShown },
{ kEventClassWindow, kEventWindowClose },
{ kEventClassWindow, kEventWindowGetClickModality },
{ kEventClassWindow, kEventWindowHandleDeactivate },
{ kEventClassWindow, kEventWindowDeactivated },
};
// Our window event handler callback thunk
pascal OSStatus VSTEffectDialog::WindowEventHandler(EventHandlerCallRef handler, EventRef event, void *data)
{
return ((VSTEffectDialog *)data)->OnWindowEvent(handler, event);
}
// Our window event handler
OSStatus VSTEffectDialog::OnWindowEvent(EventHandlerCallRef handler, EventRef event)
{
// Get the current window in from of all the rest non-floaters.
WindowRef frontwin = FrontNonFloatingWindow();
// Get the target window of the event
WindowRef evtwin = 0;
GetEventParameter(event,
kEventParamDirectObject,
typeWindowRef,
NULL,
sizeof(evtwin),
NULL,
&evtwin);
#if defined(DEBUG_VST)
int cls = GetEventClass(event);
printf("WINDOW class %4.4s kind %d ewin %p owin %p mwin %p anf %p fnf %p\n",
&cls,
GetEventKind(event),
evtwin,
mOverlayRef,
mWindowRef,
ActiveNonFloatingWindow(),
frontwin);
#endif
// Only kEventClassWindow event at this poine
switch (GetEventKind(event))
{
// If we don't capture the close event Audacity, will crash attermination
// since the window is still on the wxWidgets toplevel window lists, but
// it's already gone.
case kEventWindowClose:
{
RemoveHandler();
Close();
return noErr;
}
break;
// This is where we determine if the effect has created a window above
// ours. Since the overlay is created on top of our window, we look at
// the topmost window to see if it is different that ours. If so, then
// we assume an overlay has been created and install the event handler
// on the overlay.
case kEventWindowShown:
{
// Have an overlay?
mOverlayRef = FrontNonFloatingWindow();
if (mOverlayRef != mWindowRef)
{
// Install the handler
mOverlayEventHandlerUPP = NewEventHandlerUPP(OverlayEventHandler);
InstallWindowEventHandler(mOverlayRef,
mOverlayEventHandlerUPP,
GetEventTypeCount(OverlayEventList),
OverlayEventList,
this,
&mOverlayEventHandlerRef);
}
}
break;
// The system is asking if the target of an upcoming event
// should be passed to the overlay window or not.
//
// We allow it when the overlay window or our window is the
// curret top window. Any other windows would mean that a
// modal dialog box has been open on top and we should block.
case kEventWindowGetClickModality:
{
// No overlay present, so leave this up to the system
if (mOverlayRef == mWindowRef) {
break;
}
HIModalClickResult res = kHIModalClickIsModal;
WindowRef ref = mWindowRef;
// Allow it to pass?
if (frontwin == mWindowRef | frontwin == mOverlayRef) {
res |= kHIModalClickAllowEvent; // | kHIModalClickRaiseWindow;
}
// No, block it
else {
res |= kHIModalClickAnnounce;
ref = frontwin;
}
// Get the kind of modal block from the modal window
WindowModality kind;
GetWindowModality(ref, &kind, NULL);
// Set the return parameters
SetEventParameter(event,
kEventParamWindowModality,
typeWindowRef,
sizeof(kind),
&kind);
SetEventParameter(event,
kEventParamModalWindow,
typeWindowRef,
sizeof(ref),
&ref);
SetEventParameter(event,
kEventParamModalClickResult,
typeModalClickResult,
sizeof(res),
&res);
// The mouse click will activate the our window, but we want any
// control underneath the mouse to get the click instead. Activating
// the main window here will allow the click to pass through to the
// control.
ActivateWindow(ref, TRUE);
return noErr;
}
break;
// Instead of deactivating our window, we actually do the opposite by
// activate it. This helps to ensure it says highlighted and raised.
case kEventWindowHandleDeactivate:
case kEventWindowDeactivated:
{
// No overlay present, so leave this up to the system
if (mOverlayRef == mWindowRef) {
break;
}
ActivateWindow(mWindowRef, TRUE);
return noErr;
}
break;
}
return eventNotHandledErr;
}
#endif
VSTEffectDialog::VSTEffectDialog(wxWindow *parent,
const wxString & title,
VSTEffect *effect,
AEffect *aeffect)
const wxString & title,
VSTEffect *effect,
AEffect *aeffect)
: wxDialog(parent, wxID_ANY, title),
mEffect(effect),
mAEffect(aeffect)
@ -961,9 +1257,16 @@ VSTEffectDialog::VSTEffectDialog(wxWindow *parent,
mSliders = NULL;
mDisplays = NULL;
mLabels = NULL;
#if defined(__WXMAC__)
mHandlerUPP = NULL;
mHandlerRef = NULL;
mOverlayRef = 0;
mOverlayEventHandlerUPP = 0;
mOverlayEventHandlerRef = 0;
mOverlayEventsBlocked = true;
mWindowRef = 0;
mWindowEventHandlerUPP = 0;
mWindowEventHandlerRef = 0;
#endif
// Determine if the VST editor is supposed to be used or not
@ -1003,15 +1306,25 @@ VSTEffectDialog::~VSTEffectDialog()
void VSTEffectDialog::RemoveHandler()
{
#if defined(__WXMAC__)
if (mHandlerRef) {
::RemoveEventHandler(mHandlerRef);
mHandlerRef = NULL;
if (mOverlayEventHandlerRef) {
::RemoveEventHandler(mOverlayEventHandlerRef);
mOverlayEventHandlerRef = 0;
}
if (mOverlayEventHandlerUPP) {
DisposeEventHandlerUPP(mOverlayEventHandlerUPP);
mOverlayEventHandlerUPP = 0;
}
if (mWindowEventHandlerRef) {
::RemoveEventHandler(mWindowEventHandlerRef);
mWindowEventHandlerRef = 0;
MacInstallTopLevelWindowEventHandler();
}
if (mHandlerUPP) {
DisposeEventHandlerUPP(mHandlerUPP);
mHandlerUPP = NULL;
if (mWindowEventHandlerUPP) {
DisposeEventHandlerUPP(mWindowEventHandlerUPP);
mWindowEventHandlerUPP = 0;
}
#endif
}
@ -1027,44 +1340,70 @@ void VSTEffectDialog::BuildFancy()
mEffect->callDispatcher(effEditGetRect, 0, 0, &rect, 0.0);
#if defined(__WXMAC__)
mWindowRef = (WindowRef) MacGetTopLevelWindowRef();
// Install the event handler on our window
mWindowEventHandlerUPP = NewEventHandlerUPP(WindowEventHandler);
InstallWindowEventHandler(mWindowRef,
mWindowEventHandlerUPP,
GetEventTypeCount(WindowEventList),
WindowEventList,
this,
&mWindowEventHandlerRef);
// Find the content view within our window
HIViewRef view;
WindowRef win = (WindowRef) MacGetTopLevelWindowRef();
HIViewFindByID(HIViewGetRoot(win), kHIViewWindowContentID, &view);
HIViewFindByID(HIViewGetRoot(mWindowRef), kHIViewWindowContentID, &view);
mEffect->callDispatcher(effEditOpen, 0, 0, win, 0.0);
// And ask the effect to add it's GUI
mEffect->callDispatcher(effEditOpen, 0, 0, mWindowRef, 0.0);
// Get the subview it created
HIViewRef subview = HIViewGetFirstSubview(view);
if (subview == NULL) {
mEffect->callDispatcher(effEditClose, 0, 0, win, 0.0);
// Doesn't seem the effect created the subview, so switch
// to the plain dialog
mEffect->callDispatcher(effEditClose, 0, 0, mWindowRef, 0.0);
mGui = false;
RemoveHandler();
BuildPlain();
return;
}
#elif defined(__WXMSW__)
wxWindow *w = new wxPanel(this, wxID_ANY);
#elif defined(__WXMSW__)
wxWindow *w = new wxPanel(this, wxID_ANY);
mEffect->callDispatcher(effEditOpen, 0, 0, w->GetHWND(), 0.0);
#else
#endif
// Get the final bounds of the effect GUI
mEffect->callDispatcher(effEditGetRect, 0, 0, &rect, 0.0);
// Build our display now
wxBoxSizer *vs = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *hs = new wxBoxSizer(wxHORIZONTAL);
wxSizerItem *si;
// Add the program bar at the top
vs->Add(BuildProgramBar(), 0, wxCENTER | wxEXPAND);
// Reserve space for the effect GUI
si = hs->Add(rect->right - rect->left, rect->bottom - rect->top);
vs->Add(hs, 0, wxCENTER);
// Add the standard button bar at the bottom
vs->Add(CreateStdButtonSizer(this, ePreviewButton|eCancelButton|eOkButton), 0, wxEXPAND);
SetSizerAndFit(vs);
// Found out where the reserved space wound up
wxPoint pos = si->GetPosition();
#if defined(__WXMAC__)
// Reposition the subview into the reserved space
HIViewPlaceInSuperviewAt(subview, pos.x, pos.y);
// Some VST effects do not work unless the default handler is removed since
@ -1072,20 +1411,11 @@ void VSTEffectDialog::BuildFancy()
// done last since proper window sizing will not occur otherwise.
::RemoveEventHandler((EventHandlerRef)MacGetEventHandler());
// Install a bare minimum handler so we can capture the window close event. If
// it's not captured, we will crash at Audacity termination since the window
// is still on the wxWidgets toplevel window lists, but it's already gone.
mHandlerUPP = NewEventHandlerUPP(EventHandler);
InstallWindowEventHandler(win,
mHandlerUPP,
GetEventTypeCount(eventList),
eventList,
this,
&mHandlerRef);
#elif defined(__WXMSW__)
w->SetPosition(pos);
w->SetSize(si->GetSize());
#else
#endif
}
@ -1222,15 +1552,15 @@ wxSizer *VSTEffectDialog::BuildProgramBar()
mProgram->SetName(_("Presets"));
hs->Add(mProgram, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxButton *bt = new wxButton(this, ID_VST_LOAD, _("Load"));
wxButton *bt = new wxButton(this, ID_VST_LOAD, _("&Load"));
hs->Add(bt, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
bt = new wxButton(this, ID_VST_SAVE, _("Save"));
bt = new wxButton(this, ID_VST_SAVE, _("&Save"));
hs->Add(bt, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
hs->AddStretchSpacer();
bt = new wxButton(this, ID_VST_SETTINGS, _("Settings"));
bt = new wxButton(this, ID_VST_SETTINGS, _("S&ettings..."));
hs->Add(bt, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 5);
return hs;