diff options
author | Damien Zammit <damien@zamaudio.com> | 2020-04-19 13:50:55 +1000 |
---|---|---|
committer | Damien Zammit <damien@zamaudio.com> | 2020-04-19 13:50:55 +1000 |
commit | 5a3261feb9047312ab60e4ccecb98b53bea0350e (patch) | |
tree | 9a26e276ee9c844b296f43caaefaee60753069cf | |
parent | e2bfdea7984b107228ca367813bcf6191d770d5b (diff) |
-rw-r--r-- | Makefile.plugins.mk | 7 | ||||
-rw-r--r-- | distrho/extra/String.hpp | 11 | ||||
-rw-r--r-- | distrho/src/CocoaUI/PluginAU_CocoaUI.m | 18 | ||||
-rw-r--r-- | distrho/src/DistrhoPluginAU.cpp | 1129 | ||||
-rw-r--r-- | distrho/src/DistrhoPluginAU.h | 207 |
5 files changed, 1111 insertions, 261 deletions
diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index 07889472..b104671c 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -71,6 +71,7 @@ AU_BUILD_FLAGS = \ -I$(DPF_PATH)/distrho/src/CoreAudio106/AudioUnits/AUPublic/AUBase \ -I$(DPF_PATH)/distrho/src/CoreAudio106/AudioUnits/AUPublic/Utility \ -I$(DPF_PATH)/distrho/src/CoreAudio106/PublicUtility\ + -I$(DPF_PATH)/distrho/src/CoreAudio106/AudioUnits/AUPublic/OtherBases \ -Wno-deprecated-declarations \ -Wno-four-char-constants \ -Wno-overloaded-virtual \ @@ -348,10 +349,10 @@ $(au_uiplist): $(au_rsrc): -@mkdir -p $(shell dirname $@) @echo "Creating AU rsrc for $(NAME)" - $(DPF_PATH)/utils/AuRez aufx fxfx 'dpf ' \ + $(DPF_PATH)/utils/AuRez aufx \?\?\?\? DPFx \ $(REZ_FLAGS) \ - -o $@ -M dpf -P $(NAME) -V 0.0.0 \ - -e _PluginAUEntry -v _PluginAU_CocoaUI + -o $@ -M DPF -P $(NAME) -V 0.0.0 \ + -e _PluginAUEntry -v PluginAU_CocoaUIFactory $(BUILD_DIR)/PluginAU_CocoaUI.mm.o: $(DPF_PATH)/distrho/src/CocoaUI/PluginAU_CocoaUI.m -@mkdir -p $(shell dirname $@) diff --git a/distrho/extra/String.hpp b/distrho/extra/String.hpp index 650c5bbd..273b2b8d 100644 --- a/distrho/extra/String.hpp +++ b/distrho/extra/String.hpp @@ -54,6 +54,17 @@ public: } /* + * Simple char string, with known length. + */ + explicit String(char* const strBuf, const int len) noexcept + : fBuffer(_null()), + fBufferLen(0) + { + fBuffer = strBuf; + fBufferLen = len; + } + + /* * Simple char string. */ explicit String(char* const strBuf, const bool copyData = true) noexcept diff --git a/distrho/src/CocoaUI/PluginAU_CocoaUI.m b/distrho/src/CocoaUI/PluginAU_CocoaUI.m index dec13f50..44fb79fa 100644 --- a/distrho/src/CocoaUI/PluginAU_CocoaUI.m +++ b/distrho/src/CocoaUI/PluginAU_CocoaUI.m @@ -3,7 +3,6 @@ #include <AudioToolbox/AudioToolbox.h> #include <AudioUnit/AUCocoaUIView.h> #include "DistrhoUIInternal.hpp" -#include "DistrhoPluginInternal.hpp" // Direct access #define MAX_PARAMS 100 @@ -14,7 +13,6 @@ class UIAu public: UIAu(const char* const uiTitle) : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), - fPlugin((PluginExporter *)fUI.getDirectAccess()), fHostClosed(false) { fUI.setWindowTitle(uiTitle); @@ -24,11 +22,6 @@ public: { } - uint32_t getParameterCountFromDSP() - { - return fPlugin->getParameterCount(); - } - void auui_showhide(bool show) { fUI.setWindowVisible(show); @@ -55,7 +48,6 @@ protected: private: UIExporter fUI; - PluginExporter *fPlugin; bool fHostClosed; // ------------------------------------------------------------------- @@ -130,8 +122,14 @@ END_NAMESPACE_DISTRHO if (mAU) [self _removeListeners]; mAU = inAU; fUIAu = new UIAu(DISTRHO_PLUGIN_NAME); - - paramCount = fUIAu->getParameterCountFromDSP(); + void *data = NULL; + + AudioUnitGetProperty (inAU, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + data, + ¶mCount); UInt32 i; for (i = 0; i < paramCount; ++i) { diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 9c5a21bd..fc7cc5e0 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -14,221 +14,483 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "DistrhoPluginInternal.hpp" +#include "DistrhoPluginAU.h" -#include "CoreAudio106/AudioUnits/AUPublic/OtherBases/AUEffectBase.h" +START_NAMESPACE_DISTRHO -// ----------------------------------------------------------------------- +PluginAU::PluginAU (AudioUnit component) + : MusicDeviceBase (component, 1, 1), + fLastValuesInit(), + fPlugin(this, writeMidiCallback), + fLastParameterValues(nullptr) +{ + inParameterChangedCallback = false; -START_NAMESPACE_DISTRHO + short configs[][2] = {{ DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS }}; + const int numConfigs = sizeof (configs) / sizeof (short[2]); -// #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT -static const writeMidiFunc writeMidiCallback = nullptr; -// #endif + fPlugin.setBufferSize(1024, true); + fPlugin.setSampleRate(44100.0, true); -// ----------------------------------------------------------------------- + if (const uint32_t count = fPlugin.getParameterCount()) + { + fPortControls = new float*[count]; + fLastControlValues = new float[count]; -struct LastValuesInit { - LastValuesInit() + for (uint32_t i=0; i < count; ++i) + { + fPortControls[i] = nullptr; + fLastControlValues[i] = fPlugin.getParameterValue(i); + } + } + else { - if (d_lastBufferSize == 0) - d_lastBufferSize = kAUDefaultMaxFramesPerSlice; + fPortControls = nullptr; + fLastControlValues = nullptr; + } - if (d_isZero(d_lastSampleRate)) - d_lastSampleRate = kAUDefaultSampleRate; - }; -}; + for (int i = 0; i < numConfigs; ++i) + { + AUChannelInfo info; -// ----------------------------------------------------------------------- -// AU Plugin + info.inChannels = configs[i][0]; + info.outChannels = configs[i][1]; -class PluginAU : public AUEffectBase -{ -public: - PluginAU(AudioUnit component) - : AUEffectBase(component), - fLastValuesInit(), - fPlugin(this, writeMidiCallback), - fLastParameterValues(nullptr) - { - CreateElements(); + channelInfo.push_back (info); + } - AUElement* const globals = Globals(); - DISTRHO_SAFE_ASSERT_RETURN(globals != nullptr,); + //AddPropertyListener (kAudioUnitProperty_ContextName, auPropertyListenerDispatcher, this); - if (const uint32_t paramCount = fPlugin.getParameterCount()) - { - fLastParameterValues = new float[paramCount]; - globals->UseIndexedParameters(paramCount); + totalInChannels = DISTRHO_PLUGIN_NUM_INPUTS; + totalOutChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; - for (uint32_t i=0; i < paramCount; ++i) - { - fLastParameterValues[i] = fPlugin.getParameterValue(i); - globals->SetParameter(i, fLastParameterValues[i]); - } - } - } + addParameters(); - ~PluginAU() override + memset (&auEvent, 0, sizeof(auEvent)); + auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance(); + auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; + auEvent.mArgument.mParameter.mElement = 0; + + memset (&midiCallback, 0, sizeof(midiCallback)); + + CreateElements(); + + syncAudioUnitWithProcessor(); +} + +PluginAU::~PluginAU() override +{ + if (fLastParameterValues) { - if (fLastParameterValues) - { - delete[] fLastParameterValues; - fLastParameterValues = nullptr; - } + delete[] fLastParameterValues; + fLastParameterValues = nullptr; } - -protected: - OSStatus GetParameterValueStrings(AudioUnitScope inScope, - AudioUnitParameterID inParameterID, - CFArrayRef* outStrings) override + if (fLastControlValues) { - // TODO scalepoints support via kAudioUnitParameterFlag_ValuesHaveStrings - return kAudioUnitErr_InvalidProperty; + delete[] fLastControlValues; + fLastControlValues = nullptr; } - - OSStatus GetParameterInfo(AudioUnitScope inScope, - AudioUnitParameterID inParameterID, - AudioUnitParameterInfo& outParameterInfo) override + if (fPortControls) { - DISTRHO_SAFE_ASSERT_RETURN(inScope == kAudioUnitScope_Global, kAudioUnitErr_InvalidParameter); + delete[] fPortControls; + fPortControls = nullptr; + } - outParameterInfo.flags = kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_IsWritable; + // shut fUI; +} - // Name - { - const String& name = fPlugin.getParameterName(inParameterID); - CFStringRef cfname = CFStringCreateWithCString(kCFAllocatorDefault, name.buffer(), kCFStringEncodingUTF8); +ComponentResult +PluginAU::Initialize() override +{ + ComponentResult err; - AUBase::FillInParameterName(outParameterInfo, cfname, false); - } + if ((err = syncProcessorWithAudioUnit()) != noErr) + return err; + + if ((err = MusicDeviceBase::Initialize()) != noErr) + return err; + + pulledSucceeded = false; + + prepareToPlay(); + + return noErr; +} + +void +PluginAU::Cleanup() override +{ + fPlugin.deactivate(); + MusicDeviceBase::Cleanup(); + + midiEvents.clear(); + incomingEvents.clear(); + prepared = false; +} + +ComponentResult +PluginAU::Reset (AudioUnitScope inScope, AudioUnitElement inElement) override +{ + if (! prepared) + prepareToPlay(); + + return MusicDeviceBase::Reset (inScope, inElement); +} + +void PluginAU::prepareToPlay() +{ + fPlugin.setBufferSize (GetMaxFramesPerSlice(), true); + fPlugin.setSampleRate (getSampleRate(), true); + fPlugin.activate(); + + midiEvents.clear(); + incomingEvents.clear(); + + prepared = true; +} + +bool +PluginAU::BusCountWritable (AudioUnitScope scope) override +{ + return false; +} + +OSStatus +PluginAU::SetBusCount (AudioUnitScope scope, UInt32 count) override +{ + OSStatus err = noErr; + bool isInput; + + if ((err = scopeToDirection (scope, isInput)) != noErr) + return err; + + if (count != 1) + { + return kAudioUnitErr_PropertyNotWritable; + } - // Hints + return noErr; +} + +UInt32 +PluginAU::SupportedNumChannels (const AUChannelInfo** outInfo) override +{ + if (outInfo != nullptr) + *outInfo = &channelInfo[0]; + + return (UInt32) channelInfo.size(); +} + +ComponentResult +PluginAU::GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32& outDataSize, + Boolean& outWritable) override +{ + if (inScope == kAudioUnitScope_Global) + { + switch (inID) { - const uint32_t hints(fPlugin.getParameterHints(inParameterID)); - - // other interesting bits: - // kAudioUnitParameterFlag_OmitFromPresets for outputs? - // kAudioUnitParameterFlag_MeterReadOnly for outputs? - // kAudioUnitParameterFlag_CanRamp for log? - // kAudioUnitParameterFlag_IsHighResolution ?? - - if ((hints & kParameterIsAutomable) == 0x0) - outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; - - /* TODO: there doesn't seem to be a hint for this - if (hints & kParameterIsBoolean) - {} - else if (hints & kParameterIsInteger) - {} - */ - - if (hints & kParameterIsLogarithmic) - outParameterInfo.flags |= kAudioUnitParameterFlag_DisplayLogarithmic; + case kAudioUnitProperty_OfflineRender: + outWritable = true; + outDataSize = sizeof (UInt32); + return noErr; + + case kMusicDeviceProperty_InstrumentCount: + outDataSize = sizeof (UInt32); + outWritable = false; + return noErr; + + case kAudioUnitProperty_CocoaUI: + outDataSize = sizeof (AudioUnitCocoaViewInfo); + outWritable = true; + return noErr; + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + case kAudioUnitProperty_MIDIOutputCallbackInfo: + outDataSize = sizeof (CFArrayRef); + outWritable = false; + return noErr; + + case kAudioUnitProperty_MIDIOutputCallback: + outDataSize = sizeof (AUMIDIOutputCallbackStruct); + outWritable = true; + return noErr; + #endif + + case kAudioUnitProperty_BypassEffect: + outDataSize = sizeof (UInt32); + outWritable = true; + return noErr; + + default: break; } + } + + return MusicDeviceBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); +} - // Ranges +ComponentResult +PluginAU::GetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void* outData) override +{ + if (inScope == kAudioUnitScope_Global) + { + switch (inID) { - const ParameterRanges& ranges(fPlugin.getParameterRanges(inParameterID)); + case kAudioUnitProperty_OfflineRender: + *(UInt32*) outData = 0; + return noErr; + + case kMusicDeviceProperty_InstrumentCount: + *(UInt32*) outData = 1; + return noErr; + + case kAudioUnitProperty_BypassEffect: + *(UInt32*) outData = isBypassed ? 1 : 0; + return noErr; + + case kAudioUnitProperty_CocoaUI: + { + // Get the main DSP bundle + CFBundleRef bundle = CFBundleGetBundleWithIdentifier( + CFSTR("com.example.audiounit." DISTRHO_PLUGIN_NAME)); + if (bundle == NULL) { + return fnfErr; + } + + CFURLRef auuiURL = CFBundleCopyResourceURL(bundle, + CFSTR(DISTRHO_PLUGIN_NAME "-CocoaUI"), + CFSTR("bundle"), + NULL); + + if (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; + } + + break; + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + case kAudioUnitProperty_MIDIOutputCallbackInfo: + { + CFStringRef strs[1]; + strs[0] = CFSTR ("MIDI Callback"); + + CFArrayRef callbackArray = CFArrayCreate (nullptr, (const void**) strs, 1, &kCFTypeArrayCallBacks); + *(CFArrayRef*) outData = callbackArray; + return noErr; + } + #endif - outParameterInfo.defaultValue = ranges.def; - outParameterInfo.minValue = ranges.min; - outParameterInfo.maxValue = ranges.max; + default: + break; } + } - // Unit + return MusicDeviceBase::GetProperty (inID, inScope, inElement, outData); +} + +ComponentResult +PluginAU::SetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void* inData, + UInt32 inDataSize) override +{ + if (inScope == kAudioUnitScope_Global) + { + switch (inID) { - const String& unit = fPlugin.getParameterUnit(inParameterID); + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + case kAudioUnitProperty_MIDIOutputCallback: + if (inDataSize < sizeof (AUMIDIOutputCallbackStruct)) + return kAudioUnitErr_InvalidPropertyValue; - // TODO: map all AU unit types + if (AUMIDIOutputCallbackStruct* callbackStruct = (AUMIDIOutputCallbackStruct*) inData) + midiCallback = *callbackStruct; - if (unit == "dB") - { - outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain; - } - else + return noErr; + #endif + + case kAudioUnitProperty_BypassEffect: { - outParameterInfo.unit = kAudioUnitParameterUnit_CustomUnit; - outParameterInfo.unitName = CFStringCreateWithCString(kCFAllocatorDefault, unit.buffer(), kCFStringEncodingUTF8); + if (inDataSize < sizeof (UInt32)) + return kAudioUnitErr_InvalidPropertyValue; + + const bool newBypass = *((UInt32*) inData) != 0; + const bool currentlyBypassed = isBypassed; + + if (newBypass != currentlyBypassed) + { + isBypassed = newBypass; + + if (! currentlyBypassed && IsInitialized()) + Reset (0, 0); + } + + return noErr; } + + default: break; } + } - return noErr; + return MusicDeviceBase::SetProperty (inID, inScope, inElement, inData, inDataSize); +} + +ComponentResult +PluginAU::SaveState (CFPropertyListRef* outData) override +{ + ComponentResult err = MusicDeviceBase::SaveState (outData); + + if (err != noErr) + return err; + + CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData; + + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const UInt8*) cit->second.buffer(), (CFIndex) cit->second.length()); + + CFStringRef key = CFStringCreateWithCString (kCFAllocatorDefault, cit->first.buffer(), kCFStringEncodingUTF8); + CFDictionarySetValue (dict, key, ourState); + CFRelease (key); + CFRelease (ourState); } - OSStatus GetPropertyInfo(AudioUnitPropertyID inID, - AudioUnitScope inScope, - AudioUnitElement inElement, - UInt32& outDataSize, - Boolean& outWritable) override + return noErr; +} + +ComponentResult +PluginAU::RestoreState (CFPropertyListRef inData) override +{ { -#if DISTRHO_PLUGIN_HAS_UI - if (inID == kAudioUnitProperty_CocoaUI && inScope == kAudioUnitScope_Global) - { - d_stdout("GetPropertyInfo asked for CocoaUI"); - outDataSize = sizeof(AudioUnitCocoaViewInfo); - outWritable = false; - return noErr; - } -#endif + // Remove the data entry from the state to prevent the superclass loading the parameters + CFMutableDictionaryRef copyWithoutData = CFDictionaryCreateMutableCopy (nullptr, 0, (CFDictionaryRef) inData); + CFDictionaryRemoveValue (copyWithoutData, CFSTR (kAUPresetDataKey)); + ComponentResult err = MusicDeviceBase::RestoreState (copyWithoutData); + CFRelease (copyWithoutData); - return AUEffectBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable); + if (err != noErr) + return err; } - OSStatus GetProperty(AudioUnitPropertyID inID, - AudioUnitScope inScope, - AudioUnitElement inElement, - void* outData) override + CFDictionaryRef dict = (CFDictionaryRef) inData; + CFDataRef data = nullptr; + + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { -#if DISTRHO_PLUGIN_HAS_UI - if (inID == kAudioUnitProperty_CocoaUI && inScope == kAudioUnitScope_Global && outData != nullptr) + CFStringRef key = CFStringCreateWithCString (kCFAllocatorDefault, cit->first.buffer(), kCFStringEncodingUTF8); + bool valuePresent = CFDictionaryGetValueIfPresent (dict, key, (const void**) &data); + CFRelease (key); + + if (valuePresent) { - d_stdout("GetProperty asked for CocoaUI"); + if (data != nullptr) + { + const int numBytes = (int) CFDataGetLength (data); + char* rawBytes = (char*) CFDataGetBytePtr (data); + + if (numBytes > 0) + fStateMap[cit->first] = String(rawBytes, numBytes); + } + } + } + return noErr; +} - // Need to do a special dance just to get the path to the UI binary +bool +PluginAU::busIgnoresLayout (bool isInput, int busNr) const +{ + return true; +} - // 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; - } +ComponentResult +PluginAU::GetParameterInfo (AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo& outParameterInfo) override +{ + if (inScope == kAudioUnitScope_Global) + { + outParameterInfo.unit = kAudioUnitParameterUnit_Generic; + outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable + | kAudioUnitParameterFlag_IsReadable + | kAudioUnitParameterFlag_HasCFNameString + | kAudioUnitParameterFlag_ValuesHaveStrings); - CFURLRef auuiURL = CFBundleCopyResourceURL(bundle, - CFSTR(DISTRHO_PLUGIN_NAME "-CocoaUI"), - CFSTR("bundle"), - NULL); + const String& name = fPlugin.getParameterName(inParameterID); + const uint32_t hints(fPlugin.getParameterHints(inParameterID)); + const ParameterRanges& ranges(fPlugin.getParameterRanges(inParameterID)); - if (auuiURL == NULL) { - d_stdout("XXX auuiURL=NULL"); - return fnfErr; - } + if (name.isEmpty() || ((hints & kParameterIsAutomable) == 0)) + outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; - // Use hardcoded UI entrypoint - CFStringRef className = CFSTR("PluginAU_CocoaUIFactory"); + if ((hints & kParameterIsInteger) == 0) + outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp; - AudioUnitCocoaViewInfo info; - info.mCocoaAUViewBundleLocation = auuiURL; - info.mCocoaAUViewClass[0] = className; + if ((hints & kParameterIsInteger) == 0) + outParameterInfo.unit = ((hints & kParameterIsBoolean) != 0) + ? kAudioUnitParameterUnit_Boolean + : kAudioUnitParameterUnit_Indexed; - *((AudioUnitCocoaViewInfo *)outData) = info; + CFStringRef paramName = CFStringCreateWithCString (kCFAllocatorDefault, name.buffer(), kCFStringEncodingUTF8); + MusicDeviceBase::FillInParameterName (outParameterInfo, paramName, true); - return noErr; - } -#endif + outParameterInfo.minValue = ranges.min; + outParameterInfo.maxValue = ranges.max; + outParameterInfo.defaultValue = ranges.def; - return AUEffectBase::GetProperty (inID, inScope, inElement, outData); + return noErr; } -#if 0 - void SetParameter(AudioUnitParameterID index, - AudioUnitParameterValue value) override + return kAudioUnitErr_InvalidParameter; +} + +ComponentResult +PluginAU::GetParameter (AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32& outValue) override +{ + if (inScope == kAudioUnitScope_Global) { - d_stdout("SetParameter %u %f", index, value); + const ParameterRanges& ranges(fPlugin.getParameterRanges(inID)); + const float normValue = fPlugin.getParameterValue(inID); + + outValue = normValue * ranges.max; + return noErr; + } - const uint32_t hints(fPlugin.getParameterHints(index)); - const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); + return MusicDeviceBase::GetParameter (inID, inScope, inElement, outValue); +} + +ComponentResult +PluginAU::SetParameter (AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 inValue, + UInt32 inBufferOffsetInFrames) override +{ + if (inScope == kAudioUnitScope_Global) + { + const ParameterRanges& ranges(fPlugin.getParameterRanges(inID)); + const uint32_t hints(fPlugin.getParameterHints(inID)); + float value = inValue / ranges.max; if (hints & kParameterIsBoolean) { @@ -240,152 +502,524 @@ protected: value = std::round(value); } - fPlugin.setParameterValue(index, value); - //printf("SET: id=%d val=%f\n", index, value); + fPlugin.setParameterValue(inID, value); + + inParameterChangedCallback = true; + return noErr; } -#endif - bool SupportsTail() override + return MusicDeviceBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames); +} + +bool +PluginAU::getCurrentPosition (TimePosition& info) override +{ + info.bbt.beatsPerBar = 0; + info.bbt.beatType = 0; + info.bbt.barStartTick = 0; + info.frame = 0; + info.playing = false; + Float64 beat, bpm; + + if (AUBase::CallHostBeatAndTempo (&beat, &bpm) != noErr) { - return false; + info.bbt.beat = 0; + info.bbt.beatsPerMinute = 0; + } else { + info.bbt.beat = beat; + info.bbt.beatsPerMinute = bpm; } - OSStatus Version() override + UInt32 outDeltaSampleOffsetToNextBeat; + double outCurrentMeasureDownBeat; + float num; + UInt32 den; + + if (AUBase::CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den, &outCurrentMeasureDownBeat) == noErr) { - return fPlugin.getVersion(); + info.bbt.beatsPerBar = num; + info.bbt.beatType = (float) den; + info.bbt.barStartTick = outCurrentMeasureDownBeat; } - OSStatus ProcessBufferLists(AudioUnitRenderActionFlags& ioActionFlags, - const AudioBufferList& inBuffer, - AudioBufferList& outBuffer, - UInt32 inFramesToProcess) override + double outCurrentSampleInTimeLine, outCycleStartBeat = 0, outCycleEndBeat = 0; + Boolean playing = false, looping = false, playchanged; + + if (AUBase::CallHostTransportState (&playing, + &playchanged, + &outCurrentSampleInTimeLine, + &looping, + &outCycleStartBeat, + &outCycleEndBeat) != noErr) { - const float* srcBuffer[DISTRHO_PLUGIN_NUM_INPUTS]; - /* */ float* destBuffer[DISTRHO_PLUGIN_NUM_OUTPUTS]; + outCurrentSampleInTimeLine = lastTimeStamp.mSampleTime; + } - for (uint32_t i = 0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) - srcBuffer[i] = (const float*)inBuffer.mBuffers[i].mData; + info.playing = playing; + info.bbt.tick = (int32_t) (outCurrentSampleInTimeLine + 0.5); - for (uint32_t i = 0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) - destBuffer[i] = (float *)outBuffer.mBuffers[i].mData; + return true; +} - updateParameterInputs(); +void +PluginAU::sendAUEvent (const AudioUnitEventType type, const int paramIndex) +{ + auEvent.mEventType = type; + auEvent.mArgument.mParameter.mParameterID = paramIndex; + AUEventListenerNotify (nullptr, nullptr, &auEvent); +} - fPlugin.run(srcBuffer, destBuffer, inFramesToProcess); +void +PluginAU::setParameterValue (int index, float newValue) override +{ + if (inParameterChangedCallback) + { + inParameterChangedCallback = false; + return; + } - updateParameterOutputsAndTriggers(); + sendAUEvent (kAudioUnitEvent_ParameterValueChange, index); +} - ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; +void +PluginAU::editParameter (int index, bool started) override +{ + if (started) { + sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index); + } else { + sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index); + } +} - return noErr; +void +PluginAU::propsChanged (void) +{ + PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0); + PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0); + PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0); + PropertyChanged (kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0); + + refreshCurrentPreset(); + + PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); +} + +void +PluginAU::bypassChanged (int, float) +{ + PropertyChanged (kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); +} + +bool +PluginAU::StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) override +{ + bool ignore; + int busIdx; + + return ((! IsInitialized()) && (elementToBusIdx (scope, element, ignore, busIdx) == noErr)); +} + +bool +PluginAU::ValidFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& format) override +{ + bool isInput; + int busNr; + + // DSP Quattro incorrectly uses global scope for the ValidFormat call + if (scope == kAudioUnitScope_Global) + return ValidFormat (kAudioUnitScope_Input, element, format) + || ValidFormat (kAudioUnitScope_Output, element, format); + + if (elementToBusIdx (scope, element, isInput, busNr) != noErr) + return false; + + const int newNumChannels = static_cast<int> (format.NumberChannels()); + const int oldNumChannels = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + + if (newNumChannels == oldNumChannels) + return true; + + return false; +} + +OSStatus +PluginAU::ChangeStreamFormat (AudioUnitScope scope, + AudioUnitElement element, + const CAStreamBasicDescription& old, + const CAStreamBasicDescription& format) override +{ + bool isInput; + int busNr; + OSStatus err; + + if ((err = elementToBusIdx (scope, element, isInput, busNr)) != noErr) + return err; + + const int newNumChannels = static_cast<int> (format.NumberChannels()); + short config = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + + if (newNumChannels != config) + return kAudioUnitErr_FormatNotSupported; + + return MusicDeviceBase::ChangeStreamFormat (scope, element, old, format); +} + +ComponentResult +PluginAU::Render (AudioUnitRenderActionFlags& ioActionFlags, + const AudioTimeStamp& inTimeStamp, + const UInt32 nFrames) override +{ + lastTimeStamp = inTimeStamp; + + // prepare buffers + { + pullInputAudio (ioActionFlags, inTimeStamp, nFrames); + prepareOutputBuffers (nFrames); } - // ------------------------------------------------------------------- + ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence; - ComponentResult Initialize() override + const float* srcBuffer[DISTRHO_PLUGIN_NUM_INPUTS]; + /* */ float* destBuffer[DISTRHO_PLUGIN_NUM_OUTPUTS]; + + // set buffer pointers to minimize copying { - ComponentResult err; + int numChannels = 0; + bool interleaved = false; + AudioBufferList* buffer = nullptr; - if ((err = AUEffectBase::Initialize()) != noErr) - return err; + // use output pointers + GetAudioBufferList (false, 0, buffer, interleaved, numChannels); - // make sure things are in sync - fPlugin.setBufferSize(GetMaxFramesPerSlice(), true); - fPlugin.setSampleRate(GetSampleRate(), true); + for (int ch = 0; ch < numChannels; ++ch) + destBuffer[ch] = interleaved ? nullptr : static_cast<float*> (buffer->mBuffers[ch].mData); + - fPlugin.activate(); + // use input pointers + const bool badData = ! pulledSucceeded; - return noErr; + if (! badData) + GetAudioBufferList (true, 0, buffer, interleaved, numChannels); + + for (int ch = 0; ch < numChannels; ++ch) + srcBuffer[ch] = (interleaved || badData) ? nullptr : static_cast<const float*> (buffer->mBuffers[ch].mData); + } - void Cleanup() override + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + // swap midi buffers { - fPlugin.deactivate(); - AUEffectBase::Cleanup(); + //const ScopedLock sl (incomingMidiLock); + midiEvents.clear(); + incomingEvents.swap(midiEvents); } - // ------------------------------------------------------------------- + MidiEvent fMidiEvents[midiEvents.size()]; + for (uint32_t i = 0; i < midiEvents.size(); i++) + { + fMidiEvents[i] = midiEvents[i]; + } - UInt32 SupportedNumChannels(const AUChannelInfo** outInfo) override + // process audio/midi + fPlugin.run (&srcBuffer[0], &destBuffer[0], nFrames, fMidiEvents, midiEvents.size()); + + // process midi output + if (! midiEvents.isEmpty() && midiCallback.midiOutputCallback != nullptr) + pushMidiOutput (nFrames); + + midiEvents.clear(); + #else + // process audio only + fPlugin.run (&srcBuffer[0], &destBuffer[0], nFrames); + #endif + return noErr; +} + +OSStatus +PluginAU::HandleMidiEvent (UInt8 nStatus, + UInt8 inChannel, + UInt8 inData1, + UInt8 inData2, + UInt32 inStartFrame) override +{ + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + const uint8_t data[] = { (uint8_t) (nStatus | inChannel), + (uint8_t) inData1, + (uint8_t) inData2 }; + + //const ScopedLock sl (incomingMidiLock); + MidiEvent& midiEvent; + midiEvent.frame = inStartFrame; + midiEvent.size = 3; + midiEvent.dataExt = nullptr; + std::memcpy(midiEvent.data, data, midiEvent.size); + incomingEvents.push_back (midiEvent); + return noErr; + #else + return kAudioUnitErr_PropertyNotInUse; + #endif +} + +OSStatus +PluginAU::HandleSysEx (const UInt8* inData, UInt32 inLength) override +{ + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + //const ScopedLock sl (incomingMidiLock); + MidiEvent& midiEvent; + midiEvent.size = inLength; + midiEvent.dataExt = inData; + std::memset(midiEvent.data, 0, MidiEvent::kDataSize); + incomingEvents.push_back (midiEvent); + return noErr; + #else + return kAudioUnitErr_PropertyNotInUse; + #endif +} + +ComponentResult +PluginAU::GetPresets (CFArrayRef* outData) override +{ + if (outData != nullptr) { - static const AUChannelInfo sChannels[1] = {{ DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS }}; + const int numPrograms = fPlugin.getProgramCount(); - if (outInfo != nullptr) - *outInfo = sChannels; + clearPresetsArray(numPrograms); - return 1; + CFMutableArrayRef presetsArrayRef = CFArrayCreateMutable (nullptr, numPrograms, nullptr); + + for (int i = 0; i < numPrograms; ++i) + { + String name (fPlugin.getProgramName(i)); + if (name.isEmpty()) + name = "Untitled"; + CFStringRef presetName = CFStringCreateWithCString (kCFAllocatorDefault, name.buffer(), kCFStringEncodingUTF8); + + setPresetName(i, presetName); + + CFArrayAppendValue (presetsArrayRef, &presetsArray.at(i)); + } + + *outData = (CFArrayRef) presetsArrayRef; } - void SetMaxFramesPerSlice(UInt32 nFrames) override + return noErr; +} + +OSStatus +PluginAU::NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) override +{ + const int numPrograms = fPlugin.getProgramCount(); + const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber; + + if (chosenPresetNumber >= numPrograms) + return kAudioUnitErr_InvalidProperty; + + AUPreset chosenPreset; + CFStringRef programName = CFStringCreateWithCString (kCFAllocatorDefault, fPlugin.getProgramName(chosenPresetNumber).buffer(), kCFStringEncodingUTF8); + + chosenPreset.presetNumber = chosenPresetNumber; + chosenPreset.presetName = programName; + + fPlugin.loadProgram(chosenPresetNumber); + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) { - fPlugin.setBufferSize(nFrames, true); - AUEffectBase::SetMaxFramesPerSlice(nFrames); + if (fPlugin.isParameterOutput(i)) + continue; + + fLastControlValues[i] = fPlugin.getParameterValue(i); + + if (fPortControls[i] != nullptr) + *fPortControls[i] = fLastControlValues[i]; } - // ------------------------------------------------------------------- + SetAFactoryPresetAsCurrent (chosenPreset); -private: - LastValuesInit fLastValuesInit; - PluginExporter fPlugin; + return noErr; +} - // Temporary data - float* fLastParameterValues; +void +PluginAU::pullInputAudio (AudioUnitRenderActionFlags& flags, + const AudioTimeStamp& timestamp, + const UInt32 nFrames) noexcept +{ + const unsigned int numInputBuses = GetScope (kAudioUnitScope_Input).GetNumberOfElements(); - void updateParameterInputs() + for (unsigned int i = 0; i < numInputBuses; ++i) { - float value; - - for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + if (AUInputElement* input = GetInput (i)) { - if (! fPlugin.isParameterInput(i)) - continue; + bool succeeded = (input->PullInput (flags, timestamp, i, nFrames) == noErr); - value = GetParameter(i); + pulledSucceeded = succeeded; + } + } +} - if (d_isEqual(fLastParameterValues[i], value)) - continue; +void +PluginAU::prepareOutputBuffers (const UInt32 nFrames) noexcept +{ + const unsigned int numOutputBuses = GetScope (kAudioUnitScope_Output).GetNumberOfElements(); - fLastParameterValues[i] = value; - fPlugin.setParameterValue(i, value); - } + for (unsigned int busIdx = 0; busIdx < numOutputBuses; ++busIdx) + { + AUOutputElement* output = GetOutput (busIdx); + + if (output->WillAllocateBuffer()) + output->PrepareBuffer (nFrames); } +} + +void +PluginAU::pushMidiOutput (UInt32 nFrames) noexcept +{ + UInt32 numPackets = 0; + size_t dataSize = 0; - void updateParameterOutputsAndTriggers() + for (std::vector<MidiEvent>::iterator i = midiEvents.begin(); + i != midiEvents.end(); ++i) { - float value; + dataSize += (size_t) i->size; + ++numPackets; + } - for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) - { - if (fPlugin.isParameterOutput(i)) - { - value = fLastParameterValues[i] = fPlugin.getParameterValue(i); - SetParameter(i, value); - } - else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) - { - // NOTE: no trigger support in AU, simulate it here - value = fPlugin.getParameterRanges(i).def; + MIDIPacket* p; + const size_t packetMembersSize = sizeof (MIDIPacket) - sizeof (p->data); + const size_t packetListMembersSize = sizeof (MIDIPacketList) - sizeof (p->data); - if (d_isEqual(value, fPlugin.getParameterValue(i))) - continue; + MIDIPacketList *packetList; + packetList = (MIDIPacketList *)malloc(packetListMembersSize + packetMembersSize * numPackets + dataSize); + packetList->numPackets = numPackets; - fLastParameterValues[i] = value; - fPlugin.setParameterValue(i, value); - SetParameter(i, value); - } - } + p = packetList->packet; + + for (std::vector<MidiEvent>::iterator i = midiEvents.begin(); + i != midiEvents.end(); ++i) + { + p->timeStamp = (MIDITimeStamp) i->frame; + p->length = (UInt16) i->size; + memcpy (p->data, i->data, (size_t) i->size); + p = MIDIPacketNext (p); } - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginAU) -}; + midiCallback.midiOutputCallback (midiCallback.userData, &lastTimeStamp, 0, packetList); +} -END_NAMESPACE_DISTRHO +void +PluginAU::GetAudioBufferList (bool isInput, + int busIdx, + AudioBufferList*& bufferList, + bool& interleaved, + int& numChannels) +{ + AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output; + + AUIOElement* ioelement = GetIOElement (scope, static_cast<UInt32> (busIdx)); + bufferList = &ioelement->GetBufferList(); + + interleaved = false; + numChannels = static_cast<int> (bufferList->mNumberBuffers); +} + +OSStatus +PluginAU::scopeToDirection (AudioUnitScope scope, bool& isInput) noexcept +{ + OSStatus err; + + isInput = (scope == kAudioUnitScope_Input); + + err = ((scope != kAudioUnitScope_Input) && (scope != kAudioUnitScope_Output)) ? (OSStatus)kAudioUnitErr_InvalidScope : noErr; + return err; +} -// ----------------------------------------------------------------------- +OSStatus +PluginAU::elementToBusIdx (AudioUnitScope scope, + AudioUnitElement element, + bool& isInput, + int& busIdx) noexcept +{ + OSStatus err; + + busIdx = static_cast<int> (element); + + if ((err = scopeToDirection (scope, isInput)) != noErr) return err; + return noErr; +} + +void +PluginAU::addParameters() +{ + const int numParams = fPlugin.getParameterCount(); + int i; + + Globals()->UseIndexedParameters (numParams); -COMPONENT_ENTRY(PluginAU) + for (i = 0; i < numParams; i++) + { + fPlugin.setParameterValue(i, fPlugin.getParameterValue(i)); + } +} + +OSStatus +PluginAU::syncAudioUnitWithProcessor() +{ + OSStatus err = noErr; + + if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Input, 1)) != noErr) + return err; + + if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Output, 1)) != noErr) + return err; + + return noErr; +} + +OSStatus +PluginAU::syncProcessorWithAudioUnit() +{ + // update total channel count + totalInChannels = DISTRHO_PLUGIN_NUM_INPUTS; + totalOutChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; -// ----------------------------------------------------------------------- + return noErr; +} + +void +PluginAU::setPresetName(int i, const CFStringRef name) +{ + presetsArray.at(i).presetNumber = i; + presetsArray.at(i).presetName = name; +} + +void +PluginAU::clearPresetsArray(int n) +{ + for (int i = presetsArray.size(); --i >= 0;) + CFRelease (presetsArray[i].presetName); + + presetsArray.clear(); + + for (int i = 0; i < n; i++) + presetsArray.push_back(emptyPreset); +} + +void +PluginAU::refreshCurrentPreset() +{ + const int currentProgramNumber = 0; // Don't know curr index + const String currentProgramName = fPlugin.getProgramName (currentProgramNumber); + + AUPreset currentPreset; + CFStringRef presetName = CFStringCreateWithCString (kCFAllocatorDefault, currentProgramName.buffer(), kCFStringEncodingUTF8); + currentPreset.presetNumber = currentProgramNumber; + currentPreset.presetName = presetName; + + SetAFactoryPresetAsCurrent (currentPreset); +} + +END_NAMESPACE_DISTRHO + +COMPONENT_ENTRY (PluginAU) #include "CoreAudio106/AudioUnits/AUPublic/AUBase/AUBase.cpp" #include "CoreAudio106/AudioUnits/AUPublic/AUBase/AUDispatch.cpp" @@ -393,7 +1027,8 @@ COMPONENT_ENTRY(PluginAU) #include "CoreAudio106/AudioUnits/AUPublic/AUBase/AUOutputElement.cpp" #include "CoreAudio106/AudioUnits/AUPublic/AUBase/AUScopeElement.cpp" #include "CoreAudio106/AudioUnits/AUPublic/AUBase/ComponentBase.cpp" -#include "CoreAudio106/AudioUnits/AUPublic/OtherBases/AUEffectBase.cpp" +#include "CoreAudio106/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.cpp" +#include "CoreAudio106/AudioUnits/AUPublic/OtherBases/AUMIDIBase.cpp" #include "CoreAudio106/AudioUnits/AUPublic/Utility/AUBaseHelper.cpp" #include "CoreAudio106/AudioUnits/AUPublic/Utility/AUBuffer.cpp" #include "CoreAudio106/PublicUtility/CAAudioChannelLayout.cpp" @@ -401,5 +1036,3 @@ COMPONENT_ENTRY(PluginAU) #include "CoreAudio106/PublicUtility/CAMutex.cpp" #include "CoreAudio106/PublicUtility/CAStreamBasicDescription.cpp" #include "CoreAudio106/PublicUtility/CAVectorUnit.cpp" - -// ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginAU.h b/distrho/src/DistrhoPluginAU.h new file mode 100644 index 00000000..2619a876 --- /dev/null +++ b/distrho/src/DistrhoPluginAU.h @@ -0,0 +1,207 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> + * + * 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. + */ +#ifndef DISTRHO_PLUGIN_AU_H_ +#define DISTRHO_PLUGIN_AU_H_ + +#include "DistrhoPluginInternal.hpp" + +//#include <AudioUnit/AUCocoaUIView.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioUnitUtilities.h> +#include <CoreMIDI/MIDIServices.h> +#include "CoreAudio106/AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h" + +#define fPluginDictKey "fPluginState" + +typedef std::map<const String, String> StringMap; + +struct LastValuesInit { + LastValuesInit() + { + if (d_lastBufferSize == 0) + d_lastBufferSize = kAUDefaultMaxFramesPerSlice; + + if (d_isZero(d_lastSampleRate)) + d_lastSampleRate = kAUDefaultSampleRate; + }; +}; + + +START_NAMESPACE_DISTRHO + +static const writeMidiFunc writeMidiCallback = nullptr; + +// AU Plugin + +class PluginAU : public MusicDeviceBase +{ +public: + PluginAU (AudioUnit component); + ~PluginAU() override; + + ComponentResult Initialize() override; + + void Cleanup() override; + + ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement) override; + + void prepareToPlay(); + + bool BusCountWritable (AudioUnitScope scope) override; + + OSStatus SetBusCount (AudioUnitScope scope, UInt32 count) override; + + UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) override; + + ComponentResult GetPropertyInfo (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + UInt32& outDataSize, + Boolean& outWritable) override; + + ComponentResult GetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + void* outData) override; + + ComponentResult SetProperty (AudioUnitPropertyID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + const void* inData, + UInt32 inDataSize) override; + + ComponentResult SaveState (CFPropertyListRef* outData) override; + + ComponentResult RestoreState (CFPropertyListRef inData) override; + + bool busIgnoresLayout (bool isInput, int busNr) const; + + ComponentResult GetParameterInfo (AudioUnitScope inScope, + AudioUnitParameterID inParameterID, + AudioUnitParameterInfo& outParameterInfo) override; + + ComponentResult GetParameter (AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32& outValue) override; + + ComponentResult SetParameter (AudioUnitParameterID inID, + AudioUnitScope inScope, + AudioUnitElement inElement, + Float32 inValue, + UInt32 inBufferOffsetInFrames) override; + + bool CanScheduleParameters() const override { return false; } + + ComponentResult Version() override { return fPlugin.getVersion(); } + bool SupportsTail() override { return false; } + double getSampleRate() { return GetOutput (0)->GetStreamFormat().mSampleRate; } + +#if DISTRHO_PLUGIN_WANT_LATENCY + Float64 GetLatency() override + { + const double rate = getSampleRate(); + return rate > 0 ? fPlugin.getLatency() / rate : 0; + } +#endif + + bool getCurrentPosition (TimePosition& info) override; + + void sendAUEvent (const AudioUnitEventType type, const int paramIndex); + + void setParameterValue (int index, float newValue) override; + + void editParameter (int index, bool started) override; + + void propsChanged (void); + + void bypassChanged (int, float); + + bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) override; + + bool ValidFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& format) override; + + OSStatus ChangeStreamFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& old, const CAStreamBasicDescription& format) override; + + ComponentResult Render (AudioUnitRenderActionFlags& ioActionFlags, + const AudioTimeStamp& inTimeStamp, + const UInt32 nFrames) override; + + ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID*, UInt32, const MusicDeviceNoteParams&) override { return noErr; } + ComponentResult StopNote (MusicDeviceGroupID, NoteInstanceID, UInt32) override { return noErr; } + + OSStatus HandleMidiEvent (UInt8 nStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2, UInt32 inStartFrame) override; + + OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength) override; + + ComponentResult GetPresets (CFArrayRef* outData) override; + + OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) override; + + class EditorCompHolder; + struct UIAUViewClass; + struct UIAUCreationClass; + +private: + LastValuesInit fLastValuesInit; + PluginExporter fPlugin; + float* fLastParameterValues; + float* fLastControlValues; + float** fPortControls; + + std::vector<MidiEvent> midiEvents, incomingEvents; + bool prepared, isBypassed; + StringMap fStateMap; + AudioUnitEvent auEvent; + std::vector<struct AUPreset> presetsArray; + struct AUPreset emptyPreset; + AUMIDIOutputCallbackStruct midiCallback; + AudioTimeStamp lastTimeStamp; + int totalInChannels, totalOutChannels; + bool pulledSucceeded; + bool inParameterChangedCallback; + std::vector<AUChannelInfo> channelInfo; + + void pullInputAudio (AudioUnitRenderActionFlags& flags, const AudioTimeStamp& timestamp, const UInt32 nFrames) noexcept; + + void prepareOutputBuffers (const UInt32 nFrames) noexcept; + + void pushMidiOutput (UInt32 nFrames) noexcept; + + void GetAudioBufferList (bool isInput, int busIdx, AudioBufferList*& bufferList, bool& interleaved, int& numChannels); + + OSStatus scopeToDirection (AudioUnitScope scope, bool& isInput) noexcept; + + OSStatus elementToBusIdx (AudioUnitScope scope, AudioUnitElement element, bool& isInput, int& busIdx) noexcept; + + void addParameters(); + + OSStatus syncAudioUnitWithProcessor(); + + OSStatus syncProcessorWithAudioUnit(); + + void setPresetName(int i, const CFStringRef name); + + void clearPresetsArray(int n); + + void refreshCurrentPreset(); + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginAU) +}; + +END_NAMESPACE_DISTRHO + +#endif |