|
|
@@ -73,7 +73,6 @@ |
|
|
|
#define Point CarbonDummyPointName
|
|
|
|
#include "AUCarbonViewBase.h"
|
|
|
|
#undef Point
|
|
|
|
class JuceAUView;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __clang__
|
|
|
@@ -89,8 +88,6 @@ |
|
|
|
#include "../../juce_core/native/juce_osx_ObjCHelpers.h"
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
#define JuceUICreationClass JucePlugin_AUCocoaViewClassName
|
|
|
|
|
|
|
|
static Array<void*> activePlugins, activeUIs;
|
|
|
|
|
|
|
|
static const AudioUnitPropertyID juceFilterObjectPropertyID = 0x1a45ffe9;
|
|
|
@@ -175,7 +172,16 @@ public: |
|
|
|
shutdownJuce_GUI();
|
|
|
|
}
|
|
|
|
|
|
|
|
void deleteActiveEditors();
|
|
|
|
void deleteActiveEditors()
|
|
|
|
{
|
|
|
|
for (int i = activeUIs.size(); --i >= 0;)
|
|
|
|
{
|
|
|
|
id ui = (id) activeUIs.getUnchecked(i);
|
|
|
|
|
|
|
|
if (JuceUIViewClass::getAU (ui) == this)
|
|
|
|
JuceUIViewClass::deleteEditor (ui);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,
|
|
|
@@ -253,13 +259,14 @@ public: |
|
|
|
{
|
|
|
|
JUCE_AUTORELEASEPOOL
|
|
|
|
|
|
|
|
AudioUnitCocoaViewInfo* info = (AudioUnitCocoaViewInfo*) outData;
|
|
|
|
static JuceUICreationClass cls;
|
|
|
|
|
|
|
|
const File bundleFile (File::getSpecialLocation (File::currentApplicationFile));
|
|
|
|
NSString* bundlePath = [NSString stringWithUTF8String: (const char*) bundleFile.getFullPathName().toUTF8()];
|
|
|
|
NSBundle* b = [NSBundle bundleWithPath: bundlePath];
|
|
|
|
|
|
|
|
info->mCocoaAUViewClass[0] = (CFStringRef) [nsStringLiteral (JUCE_STRINGIFY (JuceUICreationClass)) retain];
|
|
|
|
AudioUnitCocoaViewInfo* info = static_cast <AudioUnitCocoaViewInfo*> (outData);
|
|
|
|
info->mCocoaAUViewClass[0] = (CFStringRef) [juceStringToNS (class_getName (cls.cls)) retain];
|
|
|
|
info->mCocoaAUViewBundleLocation = (CFURLRef) [[NSURL fileURLWithPath: [b bundlePath]] retain];
|
|
|
|
|
|
|
|
return noErr;
|
|
|
@@ -841,7 +848,6 @@ public: |
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
OSStatus HandleMidiEvent (UInt8 nStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2,
|
|
|
|
#if defined (MAC_OS_X_VERSION_10_5)
|
|
|
|
UInt32 inStartFrame)
|
|
|
@@ -931,121 +937,101 @@ protected: |
|
|
|
[view setNeedsDisplay: YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
//==============================================================================
|
|
|
|
ScopedPointer<AudioProcessor> juceFilter;
|
|
|
|
AudioSampleBuffer bufferSpace;
|
|
|
|
HeapBlock <float*> channels;
|
|
|
|
MidiBuffer midiEvents, incomingEvents;
|
|
|
|
bool prepared;
|
|
|
|
SMPTETime lastSMPTETime;
|
|
|
|
AUChannelInfo channelInfo [numChannelConfigs];
|
|
|
|
AudioUnitEvent auEvent;
|
|
|
|
mutable Array<AUPreset> presetsArray;
|
|
|
|
CriticalSection incomingMidiLock;
|
|
|
|
|
|
|
|
void clearPresetsArray() const
|
|
|
|
class EditorCompHolder : public Component
|
|
|
|
{
|
|
|
|
for (int i = presetsArray.size(); --i >= 0;)
|
|
|
|
CFRelease (presetsArray.getReference(i).presetName);
|
|
|
|
|
|
|
|
presetsArray.clear();
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
EditorCompHolder (AudioProcessorEditor* const editor)
|
|
|
|
{
|
|
|
|
setSize (editor->getWidth(), editor->getHeight());
|
|
|
|
addAndMakeVisible (editor);
|
|
|
|
|
|
|
|
JUCE_DECLARE_NON_COPYABLE (JuceAU)
|
|
|
|
};
|
|
|
|
#if ! JucePlugin_EditorRequiresKeyboardFocus
|
|
|
|
setWantsKeyboardFocus (false);
|
|
|
|
#else
|
|
|
|
setWantsKeyboardFocus (true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class EditorCompHolder : public Component
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
EditorCompHolder (AudioProcessorEditor* const editor)
|
|
|
|
{
|
|
|
|
setSize (editor->getWidth(), editor->getHeight());
|
|
|
|
addAndMakeVisible (editor);
|
|
|
|
~EditorCompHolder()
|
|
|
|
{
|
|
|
|
deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
|
|
|
|
// have been transferred to another parent which takes over ownership.
|
|
|
|
}
|
|
|
|
|
|
|
|
#if ! JucePlugin_EditorRequiresKeyboardFocus
|
|
|
|
setWantsKeyboardFocus (false);
|
|
|
|
#else
|
|
|
|
setWantsKeyboardFocus (true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
|
|
|
|
{
|
|
|
|
EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
|
|
|
|
NSRect r = NSMakeRect (0, 0, editorCompHolder->getWidth(), editorCompHolder->getHeight());
|
|
|
|
|
|
|
|
~EditorCompHolder()
|
|
|
|
{
|
|
|
|
deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
|
|
|
|
// have been transferred to another parent which takes over ownership.
|
|
|
|
}
|
|
|
|
static JuceUIViewClass cls;
|
|
|
|
NSView* view = [[cls.createInstance() initWithFrame: r] autorelease];
|
|
|
|
|
|
|
|
static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
|
|
|
|
{
|
|
|
|
EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
|
|
|
|
NSRect r = NSMakeRect (0, 0, editorCompHolder->getWidth(), editorCompHolder->getHeight());
|
|
|
|
JuceUIViewClass::setFilter (view, filter);
|
|
|
|
JuceUIViewClass::setAU (view, au);
|
|
|
|
JuceUIViewClass::setEditor (view, editorCompHolder);
|
|
|
|
|
|
|
|
static JuceUIViewClass cls;
|
|
|
|
NSView* view = [[cls.createInstance() initWithFrame: r] autorelease];
|
|
|
|
[view setHidden: NO];
|
|
|
|
[view setPostsFrameChangedNotifications: YES];
|
|
|
|
|
|
|
|
JuceUIViewClass::setFilter (view, filter);
|
|
|
|
JuceUIViewClass::setAU (view, au);
|
|
|
|
JuceUIViewClass::setEditor (view, editorCompHolder);
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver: view
|
|
|
|
selector: @selector (applicationWillTerminate:)
|
|
|
|
name: NSApplicationWillTerminateNotification
|
|
|
|
object: nil];
|
|
|
|
activeUIs.add (view);
|
|
|
|
|
|
|
|
[view setHidden: NO];
|
|
|
|
[view setPostsFrameChangedNotifications: YES];
|
|
|
|
editorCompHolder->addToDesktop (0, (void*) view);
|
|
|
|
editorCompHolder->setVisible (view);
|
|
|
|
return view;
|
|
|
|
}
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver: view
|
|
|
|
selector: @selector (applicationWillTerminate:)
|
|
|
|
name: NSApplicationWillTerminateNotification
|
|
|
|
object: nil];
|
|
|
|
activeUIs.add (view);
|
|
|
|
void childBoundsChanged (Component*)
|
|
|
|
{
|
|
|
|
if (Component* editor = getChildComponent(0))
|
|
|
|
{
|
|
|
|
const int w = jmax (32, editor->getWidth());
|
|
|
|
const int h = jmax (32, editor->getHeight());
|
|
|
|
|
|
|
|
editorCompHolder->addToDesktop (0, (void*) view);
|
|
|
|
editorCompHolder->setVisible (view);
|
|
|
|
return view;
|
|
|
|
}
|
|
|
|
if (getWidth() != w || getHeight() != h)
|
|
|
|
setSize (w, h);
|
|
|
|
|
|
|
|
void childBoundsChanged (Component*)
|
|
|
|
{
|
|
|
|
if (Component* editor = getChildComponent(0))
|
|
|
|
{
|
|
|
|
const int w = jmax (32, editor->getWidth());
|
|
|
|
const int h = jmax (32, editor->getHeight());
|
|
|
|
|
|
|
|
if (getWidth() != w || getHeight() != h)
|
|
|
|
setSize (w, h);
|
|
|
|
|
|
|
|
NSView* view = (NSView*) getWindowHandle();
|
|
|
|
NSRect r = [[view superview] frame];
|
|
|
|
r.size.width = editor->getWidth();
|
|
|
|
r.size.height = editor->getHeight();
|
|
|
|
[[view superview] setFrame: r];
|
|
|
|
[view setFrame: NSMakeRect (0, 0, editor->getWidth(), editor->getHeight())];
|
|
|
|
[view setNeedsDisplay: YES];
|
|
|
|
NSView* view = (NSView*) getWindowHandle();
|
|
|
|
NSRect r = [[view superview] frame];
|
|
|
|
r.size.width = editor->getWidth();
|
|
|
|
r.size.height = editor->getHeight();
|
|
|
|
[[view superview] setFrame: r];
|
|
|
|
[view setFrame: NSMakeRect (0, 0, editor->getWidth(), editor->getHeight())];
|
|
|
|
[view setNeedsDisplay: YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool keyPressed (const KeyPress&)
|
|
|
|
{
|
|
|
|
if (PluginHostType().isAbletonLive())
|
|
|
|
bool keyPressed (const KeyPress&)
|
|
|
|
{
|
|
|
|
static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
|
|
|
|
NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
|
|
|
|
|
|
|
|
if (lastEventTime != eventTime)
|
|
|
|
if (PluginHostType().isAbletonLive())
|
|
|
|
{
|
|
|
|
lastEventTime = eventTime;
|
|
|
|
static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
|
|
|
|
NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
|
|
|
|
|
|
|
|
NSView* view = (NSView*) getWindowHandle();
|
|
|
|
NSView* hostView = [view superview];
|
|
|
|
NSWindow* hostWindow = [hostView window];
|
|
|
|
if (lastEventTime != eventTime)
|
|
|
|
{
|
|
|
|
lastEventTime = eventTime;
|
|
|
|
|
|
|
|
[hostWindow makeFirstResponder: hostView];
|
|
|
|
[hostView keyDown: [NSApp currentEvent]];
|
|
|
|
[hostWindow makeFirstResponder: view];
|
|
|
|
NSView* view = (NSView*) getWindowHandle();
|
|
|
|
NSView* hostView = [view superview];
|
|
|
|
NSWindow* hostWindow = [hostView window];
|
|
|
|
|
|
|
|
[hostWindow makeFirstResponder: hostView];
|
|
|
|
[hostView keyDown: [NSApp currentEvent]];
|
|
|
|
[hostWindow makeFirstResponder: view];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
JUCE_DECLARE_NON_COPYABLE (EditorCompHolder)
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
struct JuceUIViewClass : public ObjCClass <NSView>
|
|
|
@@ -1135,76 +1121,73 @@ public: |
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
JUCE_DECLARE_NON_COPYABLE (EditorCompHolder)
|
|
|
|
};
|
|
|
|
|
|
|
|
void JuceAU::deleteActiveEditors()
|
|
|
|
{
|
|
|
|
for (int i = activeUIs.size(); --i >= 0;)
|
|
|
|
//==============================================================================
|
|
|
|
struct JuceUICreationClass : public ObjCClass <NSObject>
|
|
|
|
{
|
|
|
|
id ui = (id) activeUIs.getUnchecked(i);
|
|
|
|
|
|
|
|
if (EditorCompHolder::JuceUIViewClass::getAU (ui) == this)
|
|
|
|
EditorCompHolder::JuceUIViewClass::deleteEditor (ui);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
JuceUICreationClass() : ObjCClass <NSObject> ("JUCE_AUCocoaViewClass_")
|
|
|
|
{
|
|
|
|
addMethod (@selector (interfaceVersion), interfaceVersion, @encode (unsigned int), "@:");
|
|
|
|
addMethod (@selector (description), description, @encode (NSString*), "@:");
|
|
|
|
addMethod (@selector (uiViewForAudioUnit:withSize:), uiViewForAudioUnit, @encode (NSView*), "@:", @encode (AudioUnit), @encode (NSSize));
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
@interface JuceUICreationClass : NSObject <AUCocoaUIBase>
|
|
|
|
{
|
|
|
|
}
|
|
|
|
addProtocol (@protocol (AUCocoaUIBase));
|
|
|
|
|
|
|
|
- (unsigned) interfaceVersion;
|
|
|
|
- (NSString*) description;
|
|
|
|
- (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit
|
|
|
|
withSize: (NSSize) inPreferredSize;
|
|
|
|
@end
|
|
|
|
registerClass();
|
|
|
|
}
|
|
|
|
|
|
|
|
@implementation JuceUICreationClass
|
|
|
|
private:
|
|
|
|
static unsigned int interfaceVersion (id, SEL) { return 0; }
|
|
|
|
|
|
|
|
- (unsigned) interfaceVersion
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static NSString* description (id, SEL)
|
|
|
|
{
|
|
|
|
return [NSString stringWithString: nsStringLiteral (JucePlugin_Name)];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) description
|
|
|
|
{
|
|
|
|
return [NSString stringWithString: nsStringLiteral (JucePlugin_Name)];
|
|
|
|
}
|
|
|
|
static NSView* uiViewForAudioUnit (id, SEL, AudioUnit inAudioUnit, NSSize)
|
|
|
|
{
|
|
|
|
void* pointers[2];
|
|
|
|
UInt32 propertySize = sizeof (pointers);
|
|
|
|
|
|
|
|
- (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit
|
|
|
|
withSize: (NSSize) inPreferredSize
|
|
|
|
{
|
|
|
|
void* pointers[2];
|
|
|
|
UInt32 propertySize = sizeof (pointers);
|
|
|
|
if (AudioUnitGetProperty (inAudioUnit, juceFilterObjectPropertyID,
|
|
|
|
kAudioUnitScope_Global, 0, pointers, &propertySize) == noErr)
|
|
|
|
{
|
|
|
|
if (AudioProcessor* filter = static_cast <AudioProcessor*> (pointers[0]))
|
|
|
|
if (AudioProcessorEditor* editorComp = filter->createEditorIfNeeded())
|
|
|
|
return EditorCompHolder::createViewFor (filter, static_cast <JuceAU*> (pointers[1]), editorComp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AudioUnitGetProperty (inAudioUnit,
|
|
|
|
juceFilterObjectPropertyID,
|
|
|
|
kAudioUnitScope_Global,
|
|
|
|
0,
|
|
|
|
pointers,
|
|
|
|
&propertySize) != noErr)
|
|
|
|
return nil;
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
AudioProcessor* filter = (AudioProcessor*) pointers[0];
|
|
|
|
JuceAU* au = (JuceAU*) pointers[1];
|
|
|
|
private:
|
|
|
|
//==============================================================================
|
|
|
|
ScopedPointer<AudioProcessor> juceFilter;
|
|
|
|
AudioSampleBuffer bufferSpace;
|
|
|
|
HeapBlock <float*> channels;
|
|
|
|
MidiBuffer midiEvents, incomingEvents;
|
|
|
|
bool prepared;
|
|
|
|
SMPTETime lastSMPTETime;
|
|
|
|
AUChannelInfo channelInfo [numChannelConfigs];
|
|
|
|
AudioUnitEvent auEvent;
|
|
|
|
mutable Array<AUPreset> presetsArray;
|
|
|
|
CriticalSection incomingMidiLock;
|
|
|
|
|
|
|
|
if (filter == nullptr)
|
|
|
|
return nil;
|
|
|
|
void clearPresetsArray() const
|
|
|
|
{
|
|
|
|
for (int i = presetsArray.size(); --i >= 0;)
|
|
|
|
CFRelease (presetsArray.getReference(i).presetName);
|
|
|
|
|
|
|
|
AudioProcessorEditor* editorComp = filter->createEditorIfNeeded();
|
|
|
|
if (editorComp == nullptr)
|
|
|
|
return nil;
|
|
|
|
presetsArray.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
return EditorCompHolder::createViewFor (filter, au, editorComp);
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
JUCE_DECLARE_NON_COPYABLE (JuceAU)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
#if BUILD_AU_CARBON_UI
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class JuceAUView : public AUCarbonViewBase
|
|
|
|
{
|
|
|
|
public:
|
|
|
@@ -1278,7 +1261,7 @@ private: |
|
|
|
aren't so careful) */
|
|
|
|
jassert (Component::getCurrentlyModalComponent() == nullptr);
|
|
|
|
|
|
|
|
if (EditorCompHolder* editorCompHolder = dynamic_cast <EditorCompHolder*> (windowComp->getChildComponent(0)))
|
|
|
|
if (JuceAU::EditorCompHolder* editorCompHolder = dynamic_cast <JuceAU::EditorCompHolder*> (windowComp->getChildComponent(0)))
|
|
|
|
if (AudioProcessorEditor* audioProcessEditor = dynamic_cast <AudioProcessorEditor*> (editorCompHolder->getChildComponent(0)))
|
|
|
|
juceFilter->editorBeingDeleted (audioProcessEditor);
|
|
|
|
|
|
|
@@ -1424,7 +1407,7 @@ private: |
|
|
|
private:
|
|
|
|
HIViewRef parentView;
|
|
|
|
NSWindow* hostWindow;
|
|
|
|
EditorCompHolder editor;
|
|
|
|
JuceAU::EditorCompHolder editor;
|
|
|
|
bool recursive;
|
|
|
|
};
|
|
|
|
};
|
|
|
@@ -1458,6 +1441,10 @@ private: |
|
|
|
//==============================================================================
|
|
|
|
JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)
|
|
|
|
|
|
|
|
#ifndef AUDIOCOMPONENT_ENTRY
|
|
|
|
#define JUCE_DISABLE_AU_FACTORY_ENTRY 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ! JUCE_DISABLE_AU_FACTORY_ENTRY // (You might need to disable this for old Xcode 3 builds)
|
|
|
|
JUCE_FACTORY_ENTRY (JuceAU, JucePlugin_AUExportPrefix)
|
|
|
|
#endif
|
|
|
@@ -1466,4 +1453,8 @@ JUCE_FACTORY_ENTRY (JuceAU, JucePlugin_AUExportPrefix) |
|
|
|
JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ! JUCE_DISABLE_AU_FACTORY_ENTRY
|
|
|
|
#include "AUPlugInDispatch.cpp"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|