From a8bd1a722dc6a02911044feb8c5b4ad55d7e4b28 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Sat, 2 Feb 2019 17:00:15 +1100 Subject: WIP CocoaUI for DPF (take 5) Current output of auval: ``` VERIFYING CUSTOM UI Carbon View Components Available: 0 GetPropertyInfo asked for CocoaUI GetPropertyInfo asked for CocoaUI GetProperty asked for CocoaUI Cocoa Views Available: 1 PluginAU_CocoaUIFactory PASS ``` --- Makefile.plugins.mk | 49 ++++++-- distrho/DistrhoUIMain.cpp | 2 +- distrho/src/CocoaUI/Info.plist | 14 +++ distrho/src/CocoaUI/PluginAU_CocoaUI.m | 156 ++++++++++++++++++++++++ distrho/src/CoreAudio106/Info.plist | 2 +- distrho/src/DistrhoPluginAU.cpp | 35 +++++- distrho/src/DistrhoUIAU.cpp | 211 +++++++++++++++++++++++++++++++++ examples/Parameters/Makefile | 3 + 8 files changed, 454 insertions(+), 18 deletions(-) create mode 100644 distrho/src/CocoaUI/Info.plist create mode 100644 distrho/src/CocoaUI/PluginAU_CocoaUI.m create mode 100644 distrho/src/DistrhoUIAU.cpp diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 41241a9d..b145f6b8 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -57,10 +57,12 @@ lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT) lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT) lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT) vst = $(TARGET_DIR)/$(NAME)-vst$(LIB_EXT) -au_plugin = $(TARGET_DIR)/$(NAME).component/Contents/MacOS/$(NAME) +au_plugin = $(TARGET_DIR)/$(NAME).component/Contents/MacOS/$(PLUGIN_NAME) au_pkginfo = $(TARGET_DIR)/$(NAME).component/Contents/PkgInfo au_plist = $(TARGET_DIR)/$(NAME).component/Contents/Info.plist -au_rsrc = $(TARGET_DIR)/$(NAME).component/Contents/Resources/$(NAME).rsrc +au_rsrc = $(TARGET_DIR)/$(NAME).component/Contents/Resources/$(PLUGIN_NAME).rsrc +au_ui = $(TARGET_DIR)/$(NAME).component/Contents/Resources/$(PLUGIN_NAME)-CocoaUI.bundle/Contents/MacOS/$(PLUGIN_NAME)-CocoaUI +au_uiplist = $(TARGET_DIR)/$(NAME).component/Contents/Resources/$(PLUGIN_NAME)-CocoaUI.bundle/Contents/Info.plist # --------------------------------------------------------------------------------------------------------------------- # Set stuff needed for AU @@ -76,7 +78,7 @@ AU_BUILD_FLAGS = \ # FIXME no-overloaded-virtual should be fixed! -AU_LINK_FLAGS = \ +AU_DSP_LINK_FLAGS = \ -bundle \ -framework AudioToolbox \ -framework AudioUnit \ @@ -84,6 +86,12 @@ AU_LINK_FLAGS = \ -framework CoreServices \ -exported_symbols_list $(DPF_PATH)/distrho/src/DistrhoPluginAU.exp +AU_UI_LINK_FLAGS = \ + -bundle \ + -framework AudioToolbox \ + -framework AudioUnit \ + -framework Cocoa + # not needed yet # -I$(DPF_PATH)/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUCarbonViewBase # -I$(DPF_PATH)/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUInstrumentBase @@ -158,6 +166,7 @@ DGL_LIBS += $(DGL_SYSTEM_LIBS) ifneq ($(HAVE_DGL),true) dssi_ui = lv2_ui = +au_ui = DGL_LIBS = OBJS_UI = endif @@ -224,6 +233,11 @@ $(BUILD_DIR)/DistrhoPluginAUexport.cpp.o: $(DPF_PATH)/distrho/src/DistrhoPluginA @echo "Compiling DistrhoPluginAUexport.cpp" $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ +$(BUILD_DIR)/DistrhoUIMain_AU.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp + -@mkdir -p $(BUILD_DIR) + @echo "Compiling DistrhoUIMain.cpp (AU)" + @$(CXX) $< $(BUILD_CXX_FLAGS) -Wno-unused-parameter -DDISTRHO_PLUGIN_TARGET_AU -c -o $@ + # --------------------------------------------------------------------------------------------------------------------- # JACK @@ -305,19 +319,20 @@ endif # AU ifneq ($(CROSS_COMPILING),true) -au: $(au_plugin) $(au_pkginfo) $(au_plist) $(au_rsrc) +au: $(au_plugin) $(au_pkginfo) $(au_plist) $(au_ui) $(au_uiplist) $(au_rsrc) else -au: $(au_plugin) $(au_pkginfo) $(au_plist) +au: $(au_plugin) $(au_pkginfo) $(au_plist) $(au_ui) $(au_uiplist) endif -ifeq ($(HAVE_DGL),true) -$(au_plugin): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.o $(BUILD_DIR)/DistrhoUIMain_AU.cpp.o $(DGL_LIB) -else $(au_plugin): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.o -endif -@mkdir -p $(shell dirname $@) @echo "Creating AU plugin for $(NAME)" - $(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(AU_LINK_FLAGS) $(DGL_LIBS) -o $@ + $(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(AU_DSP_LINK_FLAGS) -o $@ + +$(au_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_AU.cpp.o $(BUILD_DIR)/PluginAU_CocoaUI.mm.o $(DGL_LIB) + -@mkdir -p $(shell dirname $@) + @echo "Creating AU plugin for $(NAME)" + $(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(AU_UI_LINK_FLAGS) $(DGL_LIBS) -o $@ $(au_pkginfo): -@mkdir -p $(shell dirname $@) @@ -326,8 +341,13 @@ $(au_pkginfo): $(au_plist): -@mkdir -p $(shell dirname $@) - @echo "Creating AU Info.plist for $(NAME)" - sed -e "s/X-DPF-EXECUTABLE-DPF-X/$(NAME)/g" $(DPF_PATH)/distrho/src/CoreAudio106/Info.plist > $@ + @echo "Creating AU DSP Info.plist for $(NAME)" + sed -e "s/X-DPF-EXECUTABLE-DPF-X/$(PLUGIN_NAME)/g" $(DPF_PATH)/distrho/src/CoreAudio106/Info.plist > $@ + +$(au_uiplist): + -@mkdir -p $(shell dirname $@) + @echo "Creating AU UI Info.plist for $(NAME)" + sed -e "s/X-DPF-EXECUTABLE-DPF-X/$(PLUGIN_NAME)/g" $(DPF_PATH)/distrho/src/CocoaUI/Info.plist > $@ $(au_rsrc): $(BUILD_DIR)/step2.rsrc -@mkdir -p $(shell dirname $@) @@ -364,6 +384,11 @@ $(BUILD_DIR)/DistrhoPluginInfo.r: $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginAUexport $(BUILD_DIR)/DistrhoPluginInfoGenerator "$(BUILD_DIR)" && \ rm $(BUILD_DIR)/DistrhoPluginInfoGenerator +$(BUILD_DIR)/PluginAU_CocoaUI.mm.o: $(DPF_PATH)/distrho/src/CocoaUI/PluginAU_CocoaUI.m + -@mkdir -p $(shell dirname $@) + @echo "Compiling Cocoa UI for $(NAME)" + $(CXX) $< $(BUILD_CXX_FLAGS) -Wno-unused-parameter -ObjC++ -c -o $@ + # --------------------------------------------------------------------------------------------------------------------- -include $(OBJS_DSP:%.o=%.d) diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index 86b90910..2b75c3d7 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -17,7 +17,7 @@ #include "src/DistrhoUI.cpp" #if defined(DISTRHO_PLUGIN_TARGET_AU) -// nothing +# include "src/DistrhoUIAU.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_CARLA) // nothing #elif defined(DISTRHO_PLUGIN_TARGET_JACK) diff --git a/distrho/src/CocoaUI/Info.plist b/distrho/src/CocoaUI/Info.plist new file mode 100644 index 00000000..472ef07d --- /dev/null +++ b/distrho/src/CocoaUI/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + X-DPF-EXECUTABLE-DPF-X-CocoaUI + CFBundleIdentifier + com.example.audiounit.X-DPF-EXECUTABLE-DPF-X + CFBundlePackageType + BNDL + NSPrincipalClass + PluginAU_CocoaUIFactory + + diff --git a/distrho/src/CocoaUI/PluginAU_CocoaUI.m b/distrho/src/CocoaUI/PluginAU_CocoaUI.m new file mode 100644 index 00000000..ee8da585 --- /dev/null +++ b/distrho/src/CocoaUI/PluginAU_CocoaUI.m @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#define MAX_PARAMS 100 + +@interface PluginAU_CocoaUI : NSView +{ + AudioUnit mAU; + AudioUnitParameter mParameter[MAX_PARAMS]; + AUParameterListenerRef mParameterListener; + UInt32 paramCount; +} + +#pragma mark ____ PUBLIC FUNCTIONS ____ +- (void)setAU:(AudioUnit)inAU; + +#pragma mark ____ PRIVATE FUNCTIONS +- (void)_synchronizeUIWithParameterValues; +- (void)_addListeners; +- (void)_removeListeners; + +#pragma mark ____ LISTENER CALLBACK DISPATCHEE ____ +- (void)_parameterListener:(void *)inObject + parameter:(const AudioUnitParameter *)inParameter + value:(Float32)inValue; + +@end + + +@implementation PluginAU_CocoaUI + +#pragma mark ____ (INIT /) DEALLOC ____ +- (void)dealloc { + [self _removeListeners]; + [super dealloc]; +} + +#pragma mark ____ PUBLIC FUNCTIONS ____ +- (void)setAU:(AudioUnit)inAU { + // remove previous listeners + if (mAU) [self _removeListeners]; + mAU = inAU; + + paramCount = 1; //globalUI->dspPtr.getParameterCount(); + + UInt32 i; + for (i = 0; i < paramCount; ++i) { + mParameter[i].mAudioUnit = inAU; + mParameter[i].mParameterID = i; + mParameter[i].mScope = kAudioUnitScope_Global; + mParameter[i].mElement = 0; + } + + //auUI* dspUI = [self dspUI]; + + // add new listeners + [self _addListeners]; + + // initial setup + [self _synchronizeUIWithParameterValues]; + +} + +#pragma mark ____ LISTENER CALLBACK DISPATCHER ____ +void ParameterListenerDispatcher (void *inRefCon, void *inObject, + const AudioUnitParameter *inParameter, + Float32 inValue) +{ + PluginAU_CocoaUI *SELF = (PluginAU_CocoaUI *)inRefCon; + + [SELF _parameterListener:inObject parameter:inParameter value:inValue]; +} + +#pragma mark ____ PRIVATE FUNCTIONS ____ +- (void)_addListeners { + NSAssert (AUListenerCreate(ParameterListenerDispatcher, self, + CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0.100, // 100 ms + &mParameterListener ) == noErr, + @"[_addListeners] AUListenerCreate()"); + + UInt32 i; + for (i = 0; i < paramCount; ++i) { + NSAssert(AUListenerAddParameter(mParameterListener, + NULL, &mParameter[i]) == noErr, + @"[_addListeners] AUListenerAddParameter()"); + } +} + +- (void)_removeListeners { + UInt32 i; + for (i = 0; i < paramCount; ++i) { + NSAssert (AUListenerRemoveParameter(mParameterListener, + NULL, &mParameter[i]) == noErr, + @"[_removeListeners] AUListenerRemoveParameter()"); + } + + NSAssert (AUListenerDispose(mParameterListener) == noErr, + @"[_removeListeners] AUListenerDispose()"); +} + +- (void)_synchronizeUIWithParameterValues { + Float32 value; + UInt32 i; + + for (i = 0; i < paramCount; ++i) { + // only has global parameters + NSAssert (AudioUnitGetParameter(mAU, mParameter[i].mParameterID, + kAudioUnitScope_Global, 0, &value) == noErr, + @"[synchronizeUIWithParameterValues] (x.1)"); + NSAssert (AUParameterSet(mParameterListener, self, &mParameter[i], + value, 0) == noErr, + @"[synchronizeUIWithParameterValues] (x.2)"); + NSAssert (AUParameterListenerNotify (mParameterListener, self, + &mParameter[i]) == noErr, + @"[synchronizeUIWithParameterValues] (x.3)"); + } +} + +#pragma mark ____ LISTENER CALLBACK DISPATCHEE ____ +- (void)_parameterListener:(void *)inObject + parameter: (const AudioUnitParameter *)inParameter + value: (Float32)inValue +{ + //DPF: setUIParameter(inParameter->mParameterID, inValue); +} + +@end + +@interface PluginAU_CocoaUIFactory : NSObject +{ + IBOutlet PluginAU_CocoaUI *uiFreshlyLoadedView; +} + +- (NSString *) description; + +@end + +@implementation PluginAU_CocoaUIFactory + +- (unsigned) interfaceVersion { + return 0; +} + +- (NSString *) description { + return @"DPF: AU UI"; +} + +- (NSView *)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize { + PluginAU_CocoaUI *view = [[PluginAU_CocoaUI alloc] init]; + [view setAU:inAU]; + return [view autorelease]; +} + +@end diff --git a/distrho/src/CoreAudio106/Info.plist b/distrho/src/CoreAudio106/Info.plist index e0e7c89f..f9d3ee2f 100755 --- a/distrho/src/CoreAudio106/Info.plist +++ b/distrho/src/CoreAudio106/Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - com.yourcompany.dpfplugin + com.example.audiounit.X-DPF-EXECUTABLE-DPF-X CFBundleName X-DPF-EXECUTABLE-DPF-X CFBundleInfoDictionaryVersion diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 3b2fbc85..9c5a21bd 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -184,10 +184,37 @@ protected: if (inID == kAudioUnitProperty_CocoaUI && inScope == kAudioUnitScope_Global && outData != nullptr) { d_stdout("GetProperty asked for CocoaUI"); - AudioUnitCocoaViewInfo* const info = (AudioUnitCocoaViewInfo*)outData; - info->mCocoaAUViewBundleLocation = nullptr; // NSURL, CFURLRef - info->mCocoaAUViewClass[0] = nullptr; // NSString, CFStringRef - // return noErr; + + // Need to do a special dance just to get the path to the UI binary + + // Get the main DSP bundle + CFBundleRef bundle = CFBundleGetBundleWithIdentifier( + CFSTR("com.example.audiounit." DISTRHO_PLUGIN_NAME)); + if (bundle == NULL) { + d_stdout("XXX bundle=NULL"); + return fnfErr; + } + + CFURLRef auuiURL = CFBundleCopyResourceURL(bundle, + CFSTR(DISTRHO_PLUGIN_NAME "-CocoaUI"), + CFSTR("bundle"), + NULL); + + if (auuiURL == NULL) { + d_stdout("XXX auuiURL=NULL"); + return fnfErr; + } + + // Use hardcoded UI entrypoint + CFStringRef className = CFSTR("PluginAU_CocoaUIFactory"); + + AudioUnitCocoaViewInfo info; + info.mCocoaAUViewBundleLocation = auuiURL; + info.mCocoaAUViewClass[0] = className; + + *((AudioUnitCocoaViewInfo *)outData) = info; + + return noErr; } #endif diff --git a/distrho/src/DistrhoUIAU.cpp b/distrho/src/DistrhoUIAU.cpp new file mode 100644 index 00000000..dc5a61c7 --- /dev/null +++ b/distrho/src/DistrhoUIAU.cpp @@ -0,0 +1,211 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2019 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoUIInternal.hpp" + +#include "../extra/Sleep.hpp" + +START_NAMESPACE_DISTRHO + +//#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT +static const sendNoteFunc sendNoteCallback = nullptr; +//#endif + +// ----------------------------------------------------------------------- + +class UIAu +{ +public: + UIAu(const char* const uiTitle) + : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), + fHostClosed(false) + { + fUI.setWindowTitle(uiTitle); + } + + ~UIAu() + { + } + + void exec() + { + for (;;) + { + if (fHostClosed || ! fUI.idle()) + break; + + d_msleep(30); + } + } + + // ------------------------------------------------------------------- + +#if 0 +#if DISTRHO_PLUGIN_WANT_STATE + void auui_configure(const char* key, const char* value) + { + fUI.stateChanged(key, value); + } +#endif +#endif + + void auui_control(ulong index, float value) + { + fUI.parameterChanged(index, value); + } + +#if 0 +#if DISTRHO_PLUGIN_WANT_PROGRAMS + void auui_program(ulong bank, ulong program) + { + fUI.programLoaded(bank * 128 + program); + } +#endif +#endif + + void auui_samplerate(const double sampleRate) + { + fUI.setSampleRate(sampleRate, true); + } + + void auui_show() + { + fUI.setWindowVisible(true); + } + + void auui_hide() + { + fUI.setWindowVisible(false); + } + + void auui_quit() + { + fHostClosed = true; + fUI.quit(); + } + + // ------------------------------------------------------------------- + +protected: + void setParameterValue(const uint32_t rindex, const float value) + { + //FIXME fUI.setParameterValue(rindex, value); + } + + void setState(const char* const key, const char* const value) + { + } + +#if 0 +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) + { + if (fOscData.server == nullptr) + return; + if (channel > 0xF) + return; + + uint8_t mdata[4] = { + 0, + static_cast(channel + (velocity != 0 ? 0x90 : 0x80)), + note, + velocity + }; + fOscData.send_midi(mdata); + } +#endif +#endif + + void setSize(const uint width, const uint height) + { + fUI.setWindowSize(width, height); + } + +private: + UIExporter fUI; + bool fHostClosed; + + // ------------------------------------------------------------------- + // Callbacks + + #define uiPtr ((UIAu*)ptr) + + static void setParameterCallback(void* ptr, uint32_t rindex, float value) + { + uiPtr->setParameterValue(rindex, value); + } + + static void setStateCallback(void* ptr, const char* key, const char* value) + { + uiPtr->setState(key, value); + } + +#if 0 +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) + { + uiPtr->sendNote(channel, note, velocity); + } +#endif +#endif + + static void setSizeCallback(void* ptr, uint width, uint height) + { + uiPtr->setSize(width, height); + } + + #undef uiPtr +}; + +// ----------------------------------------------------------------------- + +static const char* gUiTitle = nullptr; +static UIAu* globalUI = nullptr; + +static void initUiIfNeeded() +{ + if (globalUI != nullptr) + return; + + if (d_lastUiSampleRate == 0.0) + d_lastUiSampleRate = 44100.0; + + globalUI = new UIAu(gUiTitle); +} + +END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + USE_NAMESPACE_DISTRHO + + // dummy test mode + if (argc == 1) + { + gUiTitle = "AU UI Test"; + + initUiIfNeeded(); + globalUI->auui_show(); + globalUI->exec(); + + delete globalUI; + globalUI = nullptr; + + return 0; + } +} diff --git a/examples/Parameters/Makefile b/examples/Parameters/Makefile index 92fddd17..489f1afa 100644 --- a/examples/Parameters/Makefile +++ b/examples/Parameters/Makefile @@ -9,6 +9,9 @@ NAME = d_parameters +# Matches DISTRHO_PLUGIN_NAME +PLUGIN_NAME = Parameters + # -------------------------------------------------------------- # Files to build -- cgit v1.2.3