DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 522f691ae6066f2445c7f2992b5c2c57816e216d
parent 496f832a0bf7213fba78e73ac8e5f99d019f4f87
Author: falkTX <falktx@falktx.com>
Date:   Sat, 24 Feb 2024 15:13:51 +0100

Implement full state for AU

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdistrho/src/DistrhoPluginAU.cpp | 676++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mdistrho/src/DistrhoPluginCLAP.cpp | 28++++++++--------------------
Mdistrho/src/DistrhoPluginInternal.hpp | 6++++--
Mdistrho/src/DistrhoPluginLV2.cpp | 32++++++++++----------------------
Mdistrho/src/DistrhoPluginVST2.cpp | 17+++--------------
Mdistrho/src/DistrhoPluginVST3.cpp | 28++++++++--------------------
Mdistrho/src/DistrhoUIAU.mm | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mexamples/States/DistrhoPluginInfo.h | 5++++-
Mexamples/States/Makefile | 1+
9 files changed, 675 insertions(+), 298 deletions(-)

diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp @@ -27,6 +27,8 @@ #include <AudioUnit/AudioUnit.h> #include <AudioToolbox/AudioUnitUtilities.h> + +#include <map> #include <vector> #ifndef DISTRHO_PLUGIN_AU_SUBTYPE @@ -215,6 +217,10 @@ static constexpr const requestParameterValueChangeFunc requestParameterValueChan static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; #endif +typedef std::map<const String, String> StringMap; + +// -------------------------------------------------------------------------------------------------------------------- + class PluginAU { public: @@ -235,7 +241,17 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT , fMidiEventCount(0) #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + , fCurrentProgram(0) + #endif + #if DISTRHO_PLUGIN_WANT_STATE + , fStateCount(fPlugin.getStateCount()) + #endif + { + fCurrentPreset.presetName = CFSTR("Default"); + fCurrentPreset.presetNumber = 0; + if (fParameterCount != 0) { fLastParameterValues = new float[fParameterCount]; @@ -258,10 +274,19 @@ public: std::memset(&fMidiOutput, 0, sizeof(fMidiOutput)); std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets)); #endif + + #if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0; i<fStateCount; ++i) + { + const String& dkey(fPlugin.getStateKey(i)); + fStateMap[dkey] = fPlugin.getStateDefaultValue(i); + } + #endif } ~PluginAU() { + CFRelease(fCurrentPreset.presetName); delete[] fLastParameterValues; } @@ -470,19 +495,17 @@ public: return kAudioUnitErr_InvalidProperty; #endif - #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS - case 'DPFa': + case 'DPFi': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); - outDataSize = sizeof(void*); + outDataSize = sizeof(uint16_t); outWritable = false; return noErr; - #endif case 'DPFe': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); - outDataSize = sizeof(bool); + outDataSize = sizeof(uint8_t); outWritable = true; return noErr; @@ -493,7 +516,7 @@ public: outWritable = true; return noErr; - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT case 'DPFn': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); @@ -502,15 +525,40 @@ public: return noErr; #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case 'DPFo': + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + outDataSize = sizeof(uint32_t); + outWritable = false; + return noErr; + #endif + #if DISTRHO_PLUGIN_WANT_STATE + case 'DPFl': + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + outDataSize = sizeof(CFArrayRef); + outWritable = false; + return noErr; + case 'DPFs': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); - DISTRHO_SAFE_ASSERT_RETURN(inElement != 0, kAudioUnitErr_InvalidElement); - outDataSize = inElement; + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement); + outDataSize = sizeof(CFStringRef); outWritable = true; return noErr; #endif + #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + case 'DPFa': + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + outDataSize = sizeof(void*); + outWritable = false; + return noErr; + #endif + // unwanted properties case kAudioUnitProperty_CPULoad: case kAudioUnitProperty_RenderContextObserver: @@ -535,91 +583,7 @@ public: switch (inProp) { case kAudioUnitProperty_ClassInfo: - { - CFPropertyListRef* const propList = static_cast<CFPropertyListRef*>(outData); - - CFMutableDictionaryRef clsInfo = CFDictionaryCreateMutable(nullptr, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - SInt32 value; - - value = 0; - if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) - { - CFDictionarySetValue(clsInfo, CFSTR(kAUPresetVersionKey), num); - CFRelease(num); - } - - value = kType; - if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) - { - CFDictionarySetValue(clsInfo, CFSTR(kAUPresetTypeKey), num); - CFRelease(num); - } - - value = kSubType; - if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) - { - CFDictionarySetValue(clsInfo, CFSTR(kAUPresetSubtypeKey), num); - CFRelease(num); - } - - value = kManufacturer; - if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) - { - CFDictionarySetValue(clsInfo, CFSTR(kAUPresetManufacturerKey), num); - CFRelease(num); - } - - if (const CFMutableDictionaryRef data = CFDictionaryCreateMutable(nullptr, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)) - { - // TODO save plugin state here - d_stdout("WIP GetProperty(%d:%s, %d:%s, %d, ...)", - inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); - - if (fParameterCount != 0) - { - CFMutableArrayRef params = CFArrayCreateMutable(nullptr, fParameterCount, &kCFTypeArrayCallBacks); - - for (uint32_t i=0; i<fParameterCount; ++i) - { - if (fPlugin.isParameterOutputOrTrigger(i)) - continue; - - const float valuef = fPlugin.getParameterValue(i); - CFStringRef key = CFStringCreateWithCString(nullptr, - fPlugin.getParameterSymbol(i), - kCFStringEncodingASCII); - CFNumberRef value = CFNumberCreate(nullptr, kCFNumberFloat32Type, &valuef); - CFDictionaryRef dict = CFDictionaryCreate(nullptr, - reinterpret_cast<const void**>(&key), - reinterpret_cast<const void**>(&value), - 1, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - CFArrayAppendValue(params, dict); - CFRelease(key); - CFRelease(value); - CFRelease(dict); - } - - CFDictionarySetValue(data, CFSTR("params"), params); - CFRelease(params); - } - - CFDictionarySetValue(clsInfo, CFSTR(kAUPresetDataKey), data); - CFRelease(data); - } - - CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), CFSTR("Default")); - - *propList = clsInfo; - } + *static_cast<CFPropertyListRef*>(outData) = retrieveClassInfo(); return noErr; #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 @@ -799,12 +763,7 @@ public: return noErr; case kAudioUnitProperty_PresentPreset: - { - AUPreset* const preset = static_cast<AUPreset*>(outData); - std::memset(preset, 0, sizeof(*preset)); - - preset->presetName = CFStringCreateWithCString(nullptr, "Default", kCFStringEncodingUTF8); - } + std::memcpy(outData, &fCurrentPreset, sizeof(AUPreset)); return noErr; #if DISTRHO_PLUGIN_HAS_UI @@ -839,8 +798,6 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT case kAudioUnitProperty_MIDIOutputCallbackInfo: - DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); - DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); { CFStringRef refs[1] = { CFSTR("MIDI Output") }; *static_cast<CFArrayRef*>(outData) = CFArrayCreate(nullptr, @@ -859,6 +816,50 @@ public: */ #endif + case 'DPFp': + *static_cast<float*>(outData) = fPlugin.getParameterValue(inElement); + return noErr; + + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case 'DPFo': + *static_cast<uint32_t*>(outData) = fCurrentProgram; + return noErr; + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + case 'DPFl': + if (const CFMutableArrayRef keysRef = CFArrayCreateMutable(nullptr, + fStateCount, + &kCFTypeArrayCallBacks)) + { + for (uint32_t i=0; i<fStateCount; ++i) + { + const CFStringRef keyRef = CFStringCreateWithCString(nullptr, + fPlugin.getStateKey(i), + kCFStringEncodingASCII); + CFArrayAppendValue(keysRef, keyRef); + CFRelease(keyRef); + } + + *static_cast<CFArrayRef*>(outData) = keysRef; + return noErr; + } + return kAudio_ParamError; + + case 'DPFs': + { + const String& key(fPlugin.getStateKey(inElement)); + #if DISTRHO_PLUGIN_WANT_FULL_STATE + fStateMap[key] = fPlugin.getStateValue(key); + #endif + + *static_cast<CFStringRef*>(outData) = CFStringCreateWithCString(nullptr, + fStateMap[key], + kCFStringEncodingUTF8); + } + return noErr; + #endif + #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS case 'DPFa': *static_cast<void**>(outData) = fPlugin.getInstancePointer(); @@ -886,61 +887,8 @@ public: const CFPropertyListRef propList = *static_cast<const CFPropertyListRef*>(inData); DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(propList) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue); - const CFDictionaryRef clsInfo = static_cast<CFDictionaryRef>(propList); - - CFDictionaryRef data = nullptr; - if (CFDictionaryGetValueIfPresent(clsInfo, CFSTR(kAUPresetDataKey), reinterpret_cast<const void**>(&data))) - { - DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue); - - CFArrayRef params = nullptr; - if (CFDictionaryGetValueIfPresent(data, CFSTR("params"), reinterpret_cast<const void**>(&params)) - && CFGetTypeID(params) == CFArrayGetTypeID()) - { - const CFIndex numParams = CFArrayGetCount(params); - CFStringRef keyRef; - CFNumberRef valueRef; - uint32_t index; - float value; - char* symbol = nullptr; - CFIndex symbolLen = -1; - - for (CFIndex i=0; i<numParams; ++i) - { - const CFDictionaryRef param = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(params, i)); - DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(param) == CFDictionaryGetTypeID()); - DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(param) == 1); - - keyRef = nullptr; - valueRef = nullptr; - CFDictionaryGetKeysAndValues(param, - reinterpret_cast<const void**>(&keyRef), - reinterpret_cast<const void**>(&valueRef)); - DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr); - DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr); - DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(keyRef) == CFStringGetTypeID()); - DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(valueRef) == CFNumberGetTypeID()); - DISTRHO_SAFE_ASSERT_BREAK(CFNumberGetValue(valueRef, kCFNumberFloat32Type, &value)); - - const CFIndex keyLen = CFStringGetLength(keyRef); - if (symbolLen < keyLen) - { - symbolLen = keyLen; - symbol = static_cast<char*>(std::realloc(symbol, symbolLen + 1)); - } - DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII)); - DISTRHO_SAFE_ASSERT_BREAK(fPlugin.getParameterIndexForSymbol(symbol, index)); - - fLastParameterValues[index] = value; - fPlugin.setParameterValue(index, value); - notifyListeners('DPFP', kAudioUnitScope_Global, index); - } - } - } + restoreClassInfo(static_cast<CFDictionaryRef>(propList)); } - // TODO - d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", - inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); return noErr; case kAudioUnitProperty_MakeConnection: @@ -1129,8 +1077,10 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUPreset), inDataSize, kAudioUnitErr_InvalidPropertyValue); - d_stdout("WIP SetProperty(%d:%s, %d:%s, %d, %p, %u)", inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); - // TODO + { + CFRelease(fCurrentPreset.presetName); + std::memcpy(&fCurrentPreset, inData, sizeof(AUPreset)); + } return noErr; #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT @@ -1142,18 +1092,45 @@ public: return noErr; #endif + case 'DPFi': + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint16_t), inDataSize, kAudioUnitErr_InvalidPropertyValue); + { + const uint16_t magic = *static_cast<const uint16_t*>(inData); + + if (magic != 1337) + return noErr; + + #if DISTRHO_PLUGIN_WANT_PROGRAMS + notifyListeners('DPFo', kAudioUnitScope_Global, 0); + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0; i<fStateCount; ++i) + notifyListeners('DPFs', kAudioUnitScope_Global, i); + #endif + + for (uint32_t i=0; i<fParameterCount; ++i) + notifyListeners('DPFp', kAudioUnitScope_Global, i); + } + return noErr; + case 'DPFe': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); - DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(bool), inDataSize, kAudioUnitErr_InvalidPropertyValue); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t), inDataSize, kAudioUnitErr_InvalidPropertyValue); { - const bool started = *static_cast<const bool*>(inData); + const uint8_t flag = *static_cast<const uint8_t*>(inData); + + if (flag != 1 && flag != 2) + return noErr; AudioUnitEvent event; std::memset(&event, 0, sizeof(event)); - event.mEventType = started ? kAudioUnitEvent_BeginParameterChangeGesture - : kAudioUnitEvent_EndParameterChangeGesture; + event.mEventType = flag == 1 ? kAudioUnitEvent_BeginParameterChangeGesture + : kAudioUnitEvent_EndParameterChangeGesture; event.mArgument.mParameter.mAudioUnit = fComponent; event.mArgument.mParameter.mParameterID = inElement; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; @@ -1169,6 +1146,9 @@ public: const float value = *static_cast<const float*>(inData); DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); + if (d_isEqual(fLastParameterValues[inElement], value)) + return noErr; + fLastParameterValues[inElement] = value; fPlugin.setParameterValue(inElement, value); @@ -1183,7 +1163,7 @@ public: } return noErr; - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT case 'DPFn': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); @@ -1191,6 +1171,9 @@ public: { const uint8_t* const midiData = static_cast<const uint8_t*>(inData); + if (midiData[0] == 0) + return noErr; + fNotesRingBuffer.writeCustomData(midiData, 3); fNotesRingBuffer.commitWrite(); } @@ -1200,13 +1183,29 @@ public: #if DISTRHO_PLUGIN_WANT_STATE case 'DPFs': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); - DISTRHO_SAFE_ASSERT_RETURN(inElement != 0, kAudioUnitErr_InvalidElement); - DISTRHO_SAFE_ASSERT_UINT2_RETURN(inDataSize == inElement, inDataSize, inElement, kAudioUnitErr_InvalidPropertyValue); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFStringRef), inDataSize, kAudioUnitErr_InvalidPropertyValue); { - const char* const key = static_cast<const char*>(inData); - const char* const value = key + std::strlen(key) + 1; + const CFStringRef valueRef = *static_cast<const CFStringRef*>(inData); + DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(), + kAudioUnitErr_InvalidPropertyValue); + + const CFIndex valueLen = CFStringGetLength(valueRef); + char* const value = static_cast<char*>(std::malloc(valueLen + 1)); + DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError); + DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8), + kAudioUnitErr_InvalidPropertyValue); + + const String& key(fPlugin.getStateKey(inElement)); + + // save this key as needed + if (fPlugin.wantStateKey(key)) + fStateMap[key] = value; fPlugin.setState(key, value); + + CFRelease(valueRef); + std::free(value); } return noErr; #endif @@ -1297,7 +1296,7 @@ public: const AudioUnitScope scope, const AudioUnitElement elem, const AudioUnitParameterValue value, - const UInt32 bufferOffset) + UInt32 /* bufferOffset */) { DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement); @@ -1308,8 +1307,10 @@ public: { fLastParameterValues[param] = value; fPlugin.setParameterValue(param, value); + #if DISTRHO_PLUGIN_HAS_UI // TODO flag param only, notify listeners later on bg thread (sem_post etc) - notifyListeners('DPFP', kAudioUnitScope_Global, param); + notifyListeners('DPFp', kAudioUnitScope_Global, param); + #endif } return noErr; @@ -1452,7 +1453,7 @@ public: // TODO flag param only, notify listeners later on bg thread (sem_post etc) event.mArgument.mParameter.mParameterID = i; AUEventListenerNotify(NULL, NULL, &event); - notifyListeners('DPFP', kAudioUnitScope_Global, i); + notifyListeners('DPFp', kAudioUnitScope_Global, i); } } @@ -1479,6 +1480,7 @@ public: midiEvent.data[1] = inData1; midiEvent.data[2] = inData2; + // TODO switch (inStatus) { case 0x80: @@ -1538,6 +1540,7 @@ private: const AudioUnit fComponent; // AUv2 related fields + AUPreset fCurrentPreset; OSStatus fLastRenderError; PropertyListeners fPropertyListeners; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 @@ -1565,6 +1568,15 @@ private: MIDIPacketList fMidiOutputPackets; #endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS + uint32_t fCurrentProgram; + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + const uint32_t fStateCount; + StringMap fStateMap; + #endif + // ---------------------------------------------------------------------------------------------------------------- #if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI @@ -1613,6 +1625,301 @@ private: } // ---------------------------------------------------------------------------------------------------------------- + + CFMutableDictionaryRef retrieveClassInfo() + { + CFMutableDictionaryRef clsInfo = CFDictionaryCreateMutable(nullptr, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + SInt32 value; + + value = 0; + if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) + { + CFDictionarySetValue(clsInfo, CFSTR(kAUPresetVersionKey), num); + CFRelease(num); + } + + value = kType; + if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) + { + CFDictionarySetValue(clsInfo, CFSTR(kAUPresetTypeKey), num); + CFRelease(num); + } + + value = kSubType; + if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) + { + CFDictionarySetValue(clsInfo, CFSTR(kAUPresetSubtypeKey), num); + CFRelease(num); + } + + value = kManufacturer; + if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) + { + CFDictionarySetValue(clsInfo, CFSTR(kAUPresetManufacturerKey), num); + CFRelease(num); + } + + CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fCurrentPreset.presetName); + + if (const CFMutableDictionaryRef data = CFDictionaryCreateMutable(nullptr, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) + { + #if DISTRHO_PLUGIN_WANT_PROGRAMS + const SInt32 program = fCurrentProgram; + if (const CFNumberRef programRef = CFNumberCreate(nullptr, kCFNumberSInt32Type, &program)) + { + CFDictionarySetValue(data, CFSTR("program"), programRef); + CFRelease(programRef); + } + #endif + + #if DISTRHO_PLUGIN_WANT_FULL_STATE + // Update current state + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + const String& key(cit->first); + fStateMap[key] = fPlugin.getStateValue(key); + } + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + if (const CFMutableArrayRef statesRef = CFArrayCreateMutable(nullptr, + fStateCount, + &kCFTypeArrayCallBacks)) + { + for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) + { + const String& key(cit->first); + const String& value(cit->second); + + #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && ! DISTRHO_PLUGIN_HAS_UI + bool wantStateKey = true; + + for (uint32_t i=0; i<fStateCount; ++i) + { + if (fPlugin.getStateKey(i) == key) + { + if (fPlugin.getStateHints(i) & kStateIsOnlyForUI) + wantStateKey = false; + + break; + } + } + + if (! wantStateKey) + continue; + #endif + + CFStringRef keyRef = CFStringCreateWithCString(nullptr, key, kCFStringEncodingASCII); + CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8); + + if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr, + reinterpret_cast<const void**>(&keyRef), + reinterpret_cast<const void**>(&valueRef), + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) + { + CFArrayAppendValue(statesRef, dictRef); + CFRelease(dictRef); + } + + CFRelease(keyRef); + CFRelease(valueRef); + } + + CFDictionarySetValue(data, CFSTR("states"), statesRef); + CFRelease(statesRef); + } + #endif + + if (const CFMutableArrayRef paramsRef = CFArrayCreateMutable(nullptr, + fParameterCount, + &kCFTypeArrayCallBacks)) + { + for (uint32_t i=0; i<fParameterCount; ++i) + { + if (fPlugin.isParameterOutputOrTrigger(i)) + continue; + + const float value = fPlugin.getParameterValue(i); + + CFStringRef keyRef = CFStringCreateWithCString(nullptr, + fPlugin.getParameterSymbol(i), + kCFStringEncodingASCII); + CFNumberRef valueRef = CFNumberCreate(nullptr, kCFNumberFloat32Type, &value); + + if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr, + reinterpret_cast<const void**>(&keyRef), + reinterpret_cast<const void**>(&valueRef), + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) + { + CFArrayAppendValue(paramsRef, dictRef); + CFRelease(dictRef); + } + + CFRelease(keyRef); + CFRelease(valueRef); + } + + CFDictionarySetValue(data, CFSTR("params"), paramsRef); + CFRelease(paramsRef); + } + + CFDictionarySetValue(clsInfo, CFSTR(kAUPresetDataKey), data); + CFRelease(data); + } + + return clsInfo; + } + + void restoreClassInfo(const CFDictionaryRef clsInfo) + { + CFDictionaryRef data = nullptr; + DISTRHO_SAFE_ASSERT_RETURN(CFDictionaryGetValueIfPresent(clsInfo, + CFSTR(kAUPresetDataKey), + reinterpret_cast<const void**>(&data)),); + DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(),); + + #if DISTRHO_PLUGIN_WANT_PROGRAMS + CFNumberRef programRef = nullptr; + if (CFDictionaryGetValueIfPresent(data, CFSTR("program"), reinterpret_cast<const void**>(&programRef)) + && CFGetTypeID(programRef) == CFNumberGetTypeID()) + { + SInt32 program = 0; + if (CFNumberGetValue(programRef, kCFNumberSInt32Type, &program) && program >= 0) + { + fCurrentProgram = program; + fPlugin.loadProgram(fCurrentProgram); + notifyListeners('DPFO', kAudioUnitScope_Global, 0); + } + } + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + CFArrayRef statesRef = nullptr; + if (CFDictionaryGetValueIfPresent(data, CFSTR("states"), reinterpret_cast<const void**>(&statesRef)) + && CFGetTypeID(statesRef) == CFArrayGetTypeID()) + { + const CFIndex numStates = CFArrayGetCount(statesRef); + char* key = nullptr; + char* value = nullptr; + CFIndex keyLen = -1; + CFIndex valueLen = -1; + + for (CFIndex i=0; i<numStates; ++i) + { + const CFDictionaryRef state = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(statesRef, i)); + DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(state) == CFDictionaryGetTypeID()); + DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(state) == 1); + + CFStringRef keyRef = nullptr; + CFStringRef valueRef = nullptr; + CFDictionaryGetKeysAndValues(state, + reinterpret_cast<const void**>(&keyRef), + reinterpret_cast<const void**>(&valueRef)); + DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID()); + DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID()); + + const CFIndex keyRefLen = CFStringGetLength(keyRef); + if (keyLen < keyRefLen) + { + keyLen = keyRefLen; + key = static_cast<char*>(std::realloc(key, keyLen + 1)); + } + DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, key, keyLen + 1, kCFStringEncodingASCII)); + + if (! fPlugin.wantStateKey(key)) + continue; + + const CFIndex valueRefLen = CFStringGetLength(valueRef); + if (valueLen < valueRefLen) + { + valueLen = valueRefLen; + value = static_cast<char*>(std::realloc(value, valueLen + 1)); + } + DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8)); + + const String dkey(key); + fStateMap[dkey] = value; + fPlugin.setState(key, value); + + for (uint32_t j=0; j<fStateCount; ++j) + { + if (fPlugin.getStateKey(j) == key) + { + if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) + notifyListeners('DPFS', kAudioUnitScope_Global, j); + + break; + } + } + } + + std::free(key); + std::free(value); + } + #endif + + CFArrayRef paramsRef = nullptr; + if (CFDictionaryGetValueIfPresent(data, CFSTR("params"), reinterpret_cast<const void**>(&paramsRef)) + && CFGetTypeID(paramsRef) == CFArrayGetTypeID()) + { + const CFIndex numParams = CFArrayGetCount(paramsRef); + char* symbol = nullptr; + CFIndex symbolLen = -1; + + for (CFIndex i=0; i<numParams; ++i) + { + const CFDictionaryRef param = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(paramsRef, i)); + DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(param) == CFDictionaryGetTypeID()); + DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(param) == 1); + + CFStringRef keyRef = nullptr; + CFNumberRef valueRef = nullptr; + CFDictionaryGetKeysAndValues(param, + reinterpret_cast<const void**>(&keyRef), + reinterpret_cast<const void**>(&valueRef)); + DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID()); + DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFNumberGetTypeID()); + + float value = 0.f; + DISTRHO_SAFE_ASSERT_BREAK(CFNumberGetValue(valueRef, kCFNumberFloat32Type, &value)); + + const CFIndex keyRefLen = CFStringGetLength(keyRef); + if (symbolLen < keyRefLen) + { + symbolLen = keyRefLen; + symbol = static_cast<char*>(std::realloc(symbol, symbolLen + 1)); + } + DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII)); + + for (uint32_t j=0; j<fParameterCount; ++j) + { + if (fPlugin.isParameterOutputOrTrigger(j)) + continue; + if (fPlugin.getParameterSymbol(j) != symbol) + continue; + + fLastParameterValues[j] = value; + fPlugin.setParameterValue(j, value); + notifyListeners('DPFp', kAudioUnitScope_Global, j); + break; + } + } + + std::free(symbol); + } + } + + // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT @@ -1651,7 +1958,7 @@ private: fLastParameterValues[index] = value; AUEventListenerNotify(NULL, NULL, &event); - notifyListeners('DPFP', kAudioUnitScope_Global, index); + notifyListeners('DPFp', kAudioUnitScope_Global, index); return true; } @@ -1662,14 +1969,31 @@ private: #endif #if DISTRHO_PLUGIN_WANT_STATE - bool updateState(const char*, const char*) + bool updateState(const char* const key, const char* const newValue) { - return true; + fPlugin.setState(key, newValue); + + for (uint32_t i=0; i<fStateCount; ++i) + { + if (fPlugin.getStateKey(i) == key) + { + const String dkey(key); + fStateMap[dkey] = newValue; + + if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) + notifyListeners('DPFs', kAudioUnitScope_Global, i); + + return true; + } + } + + d_stderr("Failed to find plugin state with key \"%s\"", key); + return false; } - static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) + static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const newValue) { - return static_cast<PluginAU*>(ptr)->updateState(key, value); + return static_cast<PluginAU*>(ptr)->updateState(key, newValue); } #endif @@ -1761,6 +2085,10 @@ struct AudioComponentPlugInInstance { return reinterpret_cast<AudioComponentMethod>(MIDIEvent); case kMusicDeviceSysExSelect: return reinterpret_cast<AudioComponentMethod>(SysEx); + #else + case kMusicDeviceMIDIEventSelect: + case kMusicDeviceSysExSelect: + return nullptr; #endif } diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp @@ -600,8 +600,8 @@ private: // Set state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; - const String& value = cit->second; + const String& key(cit->first); + const String& value(cit->second); // TODO skip DSP only states @@ -1433,7 +1433,7 @@ public: // Update current state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; + const String& key(cit->first); fStateMap[key] = fPlugin.getStateValue(key); } #endif @@ -1457,8 +1457,8 @@ public: for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; - const String& value = cit->second; + const String& key(cit->first); + const String& value(cit->second); // join key and value String tmpStr; @@ -1764,23 +1764,11 @@ public: { fPlugin.setState(key, value); - // check if we want to save this key - if (! fPlugin.wantStateKey(key)) - return; - - // check if key already exists - for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) + if (fPlugin.wantStateKey(key)) { - const String& dkey(it->first); - - if (dkey == key) - { - it->second = value; - return; - } + const String dkey(key); + fStateMap[dkey] = value; } - - d_stderr("Failed to find plugin state with key \"%s\"", key); } #endif diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp @@ -756,6 +756,7 @@ public: fPlugin->setParameterValue(index, value); } + /* bool getParameterIndexForSymbol(const char* const symbol, uint32_t& index) { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, false); @@ -771,6 +772,7 @@ public: return false; } + */ uint32_t getPortGroupCount() const noexcept { @@ -884,7 +886,7 @@ public: } #endif -# if DISTRHO_PLUGIN_WANT_FULL_STATE + #if DISTRHO_PLUGIN_WANT_FULL_STATE String getStateValue(const char* const key) const { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString); @@ -892,7 +894,7 @@ public: return fPlugin->getState(key); } -# endif + #endif void setState(const char* const key, const char* const value) { diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp @@ -1353,38 +1353,26 @@ private: // save this key if necessary if (fPlugin.wantStateKey(key)) - updateInternalState(key, newValue, false); + { + const String dkey(key); + fStateMap[dkey] = newValue; + } } bool updateState(const char* const key, const char* const newValue) { fPlugin.setState(key, newValue); - return updateInternalState(key, newValue, true); - } - bool updateInternalState(const char* const key, const char* const newValue, const bool sendToUI) - { // key must already exist - for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) + for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) { - const String& dkey(it->first); - - if (dkey == key) + if (fPlugin.getStateKey(i) == key) { - it->second = newValue; + const String dkey(key); + fStateMap[dkey] = newValue; - if (sendToUI) - { - for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) - { - if (fPlugin.getStateKey(i) == key) - { - if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) - fNeededUiSends[i] = true; - break; - } - } - } + if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) + fNeededUiSends[i] = true; return true; } diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp @@ -1286,22 +1286,11 @@ private: fPlugin.setState(key, value); // check if we want to save this key - if (! fPlugin.wantStateKey(key)) - return; - - // check if key already exists - for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) + if (fPlugin.wantStateKey(key)) { - const String& dkey(it->first); - - if (dkey == key) - { - it->second = value; - return; - } + const String dkey(key); + fStateMap[dkey] = value; } - - d_stderr("Failed to find plugin state with key \"%s\"", key); } #endif }; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp @@ -1180,7 +1180,7 @@ public: // Update current state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; + const String& key(cit->first); fStateMap[key] = fPlugin.getStateValue(key); } #endif @@ -1204,8 +1204,8 @@ public: for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; - const String& value = cit->second; + const String& key(cit->first); + const String& value(cit->second); // join key and value String tmpStr; @@ -2194,7 +2194,7 @@ public: // Update current state from plugin side for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; + const String& key(cit->first); fStateMap[key] = fPlugin.getStateValue(key); } #endif @@ -2203,8 +2203,8 @@ public: // Set state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { - const String& key = cit->first; - const String& value = cit->second; + const String& key(cit->first); + const String& value(cit->second); sendStateSetToUI(key, value); } @@ -2399,20 +2399,8 @@ public: // save this key as needed if (fPlugin.wantStateKey(key)) { - for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) - { - const String& dkey(it->first); - - if (dkey == key) - { - it->second = value; - std::free(key16); - std::free(value16); - return V3_OK; - } - } - - d_stderr("Failed to find plugin state with key \"%s\"", key); + const String dkey(key); + fStateMap[dkey] = value; } std::free(key16); diff --git a/distrho/src/DistrhoUIAU.mm b/distrho/src/DistrhoUIAU.mm @@ -73,28 +73,40 @@ public: { d_stdout("UI created"); - // fetch current state - UInt32 dataSize; - Boolean writable; - - dataSize = 0; - if (AudioUnitGetPropertyInfo(component, - kAudioUnitProperty_ParameterList, - kAudioUnitScope_Global, - 0, - &dataSize, - &writable) == noErr - && dataSize != 0 && dataSize % sizeof(AudioUnitParameterID) == 0) + #if DISTRHO_PLUGIN_WANT_STATE + // create state keys { - const uint32_t numParams = dataSize / sizeof(AudioUnitParameterID); - AudioUnitParameterValue value; - - for (uint32_t i=0; i<numParams; ++i) + CFArrayRef keysRef = nullptr; + UInt32 dataSize = sizeof(CFArrayRef); + if (AudioUnitGetProperty(fComponent, 'DPFl', kAudioUnitScope_Global, 0, &keysRef, &dataSize) == noErr + && dataSize == sizeof(CFArrayRef)) { - if (AudioUnitGetParameter(fComponent, i, kAudioUnitScope_Global, 0, &value) == noErr) - fUI.parameterChanged(i, value); + const CFIndex numStates = CFArrayGetCount(keysRef); + char* key = nullptr; + CFIndex keyLen = -1; + + fStateKeys.resize(numStates); + + for (CFIndex i=0; i<numStates; ++i) + { + const CFStringRef keyRef = static_cast<CFStringRef>(CFArrayGetValueAtIndex(keysRef, i)); + DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(keyRef) == CFStringGetTypeID()); + + const CFIndex keyRefLen = CFStringGetLength(keyRef); + if (keyLen < keyRefLen) + { + keyLen = keyRefLen; + key = static_cast<char*>(std::realloc(key, keyLen + 1)); + } + DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, key, keyLen + 1, kCFStringEncodingASCII)); + + fStateKeys[i] = key; + } + + std::free(key); } } + #endif // setup idle timer constexpr const CFTimeInterval interval = 60 * 0.0001; @@ -107,13 +119,25 @@ public: CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); - AudioUnitAddPropertyListener(fComponent, 'DPFP', auPropertyChangedCallback, this); + AudioUnitAddPropertyListener(fComponent, 'DPFp', auPropertyChangedCallback, this); + #if DISTRHO_PLUGIN_WANT_PROGRAMS + AudioUnitAddPropertyListener(fComponent, 'DPFo', auPropertyChangedCallback, this); + #endif + #if DISTRHO_PLUGIN_WANT_STATE + AudioUnitAddPropertyListener(fComponent, 'DPFs', auPropertyChangedCallback, this); + #endif } ~DPF_UI_AU() { d_stdout("UI destroyed"); - AudioUnitRemovePropertyListenerWithUserData(fComponent, 'DPFP', auPropertyChangedCallback, this); + AudioUnitRemovePropertyListenerWithUserData(fComponent, 'DPFp', auPropertyChangedCallback, this); + #if DISTRHO_PLUGIN_WANT_PROGRAMS + AudioUnitRemovePropertyListenerWithUserData(fComponent, 'DPFo', auPropertyChangedCallback, this); + #endif + #if DISTRHO_PLUGIN_WANT_STATE + AudioUnitRemovePropertyListenerWithUserData(fComponent, 'DPFs', auPropertyChangedCallback, this); + #endif if (fTimerRef != nullptr) { @@ -133,6 +157,10 @@ private: UIExporter fUI; + #if DISTRHO_PLUGIN_WANT_STATE + std::vector<String> fStateKeys; + #endif + // ---------------------------------------------------------------------------------------------------------------- // Idle setup @@ -149,21 +177,54 @@ private: // ---------------------------------------------------------------------------------------------------------------- // AU callbacks - void auPropertyChanged(const AudioUnitPropertyID prop, const AudioUnitElement elem) + void auParameterChanged(const AudioUnitElement elem) { - switch (prop) + float value = 0; + UInt32 dataSize = sizeof(float); + if (AudioUnitGetProperty(fComponent, 'DPFp', kAudioUnitScope_Global, elem, &value, &dataSize) == noErr + && dataSize == sizeof(float)) { - case 'DPFP': - { - AudioUnitParameterValue value; - if (AudioUnitGetParameter(fComponent, elem, kAudioUnitScope_Global, 0, &value) == noErr) - fUI.parameterChanged(elem, value); - } - break; - case 'DPFS': - break; + fUI.parameterChanged(elem, value); + } + } + + #if DISTRHO_PLUGIN_WANT_PROGRAMS + void auProgramChanged() + { + uint32_t program = 0; + UInt32 dataSize = sizeof(uint32_t); + if (AudioUnitGetProperty(fComponent, 'DPFo', kAudioUnitScope_Global, 0, &program, &dataSize) == noErr + && dataSize == sizeof(uint32_t)) + { + fUI.programLoaded(program); + } + } + #endif + + #if DISTRHO_PLUGIN_WANT_STATE + void auStateChanged(const AudioUnitElement elem) + { + DISTRHO_SAFE_ASSERT_RETURN(elem < fStateKeys.size(),); + + CFStringRef valueRef = nullptr; + UInt32 dataSize = sizeof(valueRef); + if (AudioUnitGetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, elem, &valueRef, &dataSize) == noErr + && dataSize == sizeof(CFStringRef) + && valueRef != nullptr + && CFGetTypeID(valueRef) == CFStringGetTypeID()) + { + const CFIndex valueLen = CFStringGetLength(valueRef); + char* const value = static_cast<char*>(std::malloc(valueLen + 1)); + DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8),); + + fUI.stateChanged(fStateKeys[elem], value); + + CFRelease(valueRef); + std::free(value); } } + #endif static void auPropertyChangedCallback(void* const userData, const AudioUnit component, @@ -177,7 +238,22 @@ private: DISTRHO_SAFE_ASSERT_RETURN(self->fComponent == component,); DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope,); - self->auPropertyChanged(prop, elem); + switch (prop) + { + case 'DPFp': + self->auParameterChanged(elem); + break; + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case 'DPFo': + self->auProgramChanged(); + break; + #endif + #if DISTRHO_PLUGIN_WANT_STATE + case 'DPFs': + self->auStateChanged(elem); + break; + #endif + } } // ---------------------------------------------------------------------------------------------------------------- @@ -185,7 +261,14 @@ private: void editParameter(const uint32_t rindex, const bool started) const { - AudioUnitSetProperty(fComponent, 'DPFe', kAudioUnitScope_Global, rindex, &started, sizeof(bool)); + const uint8_t flag = started ? 1 : 2; + AudioUnitSetProperty(fComponent, 'DPFe', kAudioUnitScope_Global, rindex, &flag, sizeof(uint8_t)); + + if (! started) + { + const uint8_t cancel = 0; + AudioUnitSetProperty(fComponent, 'DPFe', kAudioUnitScope_Global, rindex, &cancel, sizeof(uint8_t)); + } } static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) @@ -206,19 +289,15 @@ private: #if DISTRHO_PLUGIN_WANT_STATE void setState(const char* const key, const char* const value) { - const size_t len_key = std::strlen(key); - const size_t len_value = std::strlen(value); - const size_t len_combined = len_key + len_value + 2; - char* const data = static_cast<char*>(std::malloc(len_combined)); - DISTRHO_SAFE_ASSERT_RETURN(data != nullptr,); + const std::vector<String>::iterator it = std::find(fStateKeys.begin(), fStateKeys.end(), key); + DISTRHO_SAFE_ASSERT_RETURN(it != fStateKeys.end(),); - std::memcpy(data, key, len_key); - std::memcpy(data + len_key + 1, value, len_value); - data[len_key] = data[len_key + len_value + 1] = '\0'; - - AudioUnitSetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, len_combined, data, len_combined); - - std::free(data); + if (const CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8)) + { + const uint32_t index = it - fStateKeys.begin(); + AudioUnitSetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, index, &valueRef, sizeof(CFStringRef)); + CFRelease(valueRef); + } } static void setStateCallback(void* const ptr, const char* const key, const char* const value) @@ -232,6 +311,9 @@ private: { const uint8_t data[3] = { static_cast<uint8_t>((velocity != 0 ? 0x90 : 0x80) | channel), note, velocity }; AudioUnitSetProperty(fComponent, 'DPFn', kAudioUnitScope_Global, 0, data, sizeof(data)); + + const uint8_t cancel[3] = { 0, 0, 0 }; + AudioUnitSetProperty(fComponent, 'DPFn', kAudioUnitScope_Global, 0, cancel, sizeof(cancel)); } static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) @@ -293,6 +375,14 @@ END_NAMESPACE_DISTRHO ui = new DPF_UI_AU(component, winId, sampleRate, instancePointer); + // request data from DSP side + { + const uint16_t magic = 1337; + AudioUnitSetProperty(component, 'DPFi', kAudioUnitScope_Global, 0, &magic, sizeof(uint16_t)); + const uint16_t cancel = 0; + AudioUnitSetProperty(component, 'DPFi', kAudioUnitScope_Global, 0, &cancel, sizeof(uint16_t)); + } + return ui->getNativeView(); } diff --git a/examples/States/DistrhoPluginInfo.h b/examples/States/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> + * Copyright (C) 2012-2024 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 @@ -22,6 +22,9 @@ #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/States" #define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.states" +#define DISTRHO_PLUGIN_AU_SUBTYPE stat +#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr + #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_NUM_INPUTS 2 diff --git a/examples/States/Makefile b/examples/States/Makefile @@ -31,6 +31,7 @@ TARGETS += lv2_sep TARGETS += vst2 TARGETS += vst3 TARGETS += clap +TARGETS += au all: $(TARGETS)