diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 2f27ee21e..71b0115ec 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -27,6 +27,8 @@ #include #include + +#include #include #ifndef DISTRHO_PLUGIN_AU_SUBTYPE @@ -215,6 +217,10 @@ static constexpr const requestParameterValueChangeFunc requestParameterValueChan static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; #endif +typedef std::map StringMap; + +// -------------------------------------------------------------------------------------------------------------------- + class PluginAU { public: @@ -235,7 +241,17 @@ class PluginAU #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 @@ class PluginAU std::memset(&fMidiOutput, 0, sizeof(fMidiOutput)); std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets)); #endif + + #if DISTRHO_PLUGIN_WANT_STATE + for (uint32_t i=0; i(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(&key), - reinterpret_cast(&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(outData) = retrieveClassInfo(); return noErr; #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 @@ -799,12 +763,7 @@ class PluginAU return noErr; case kAudioUnitProperty_PresentPreset: - { - AUPreset* const preset = static_cast(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 @@ class PluginAU #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(outData) = CFArrayCreate(nullptr, @@ -859,6 +816,50 @@ class PluginAU */ #endif + case 'DPFp': + *static_cast(outData) = fPlugin.getParameterValue(inElement); + return noErr; + + #if DISTRHO_PLUGIN_WANT_PROGRAMS + case 'DPFo': + *static_cast(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(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(outData) = CFStringCreateWithCString(nullptr, + fStateMap[key], + kCFStringEncodingUTF8); + } + return noErr; + #endif + #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS case 'DPFa': *static_cast(outData) = fPlugin.getInstancePointer(); @@ -886,61 +887,8 @@ class PluginAU const CFPropertyListRef propList = *static_cast(inData); DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(propList) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue); - const CFDictionaryRef clsInfo = static_cast(propList); - - CFDictionaryRef data = nullptr; - if (CFDictionaryGetValueIfPresent(clsInfo, CFSTR(kAUPresetDataKey), reinterpret_cast(&data))) - { - DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue); - - CFArrayRef params = nullptr; - if (CFDictionaryGetValueIfPresent(data, CFSTR("params"), reinterpret_cast(¶ms)) - && 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(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(&keyRef), - reinterpret_cast(&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(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(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 @@ class PluginAU 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 @@ class PluginAU 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(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(inData); + const uint8_t flag = *static_cast(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 @@ class PluginAU const float value = *static_cast(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 @@ class PluginAU } 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 @@ class PluginAU { const uint8_t* const midiData = static_cast(inData); + if (midiData[0] == 0) + return noErr; + fNotesRingBuffer.writeCustomData(midiData, 3); fNotesRingBuffer.commitWrite(); } @@ -1200,13 +1183,29 @@ class PluginAU #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(inData); - const char* const value = key + std::strlen(key) + 1; + const CFStringRef valueRef = *static_cast(inData); + DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(), + kAudioUnitErr_InvalidPropertyValue); + + const CFIndex valueLen = CFStringGetLength(valueRef); + char* const value = static_cast(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 @@ class PluginAU 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 @@ class PluginAU { 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 @@ class PluginAU // 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 @@ class PluginAU midiEvent.data[1] = inData1; midiEvent.data[2] = inData2; + // TODO switch (inStatus) { case 0x80: @@ -1538,6 +1540,7 @@ class PluginAU const AudioUnit fComponent; // AUv2 related fields + AUPreset fCurrentPreset; OSStatus fLastRenderError; PropertyListeners fPropertyListeners; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 @@ -1565,6 +1568,15 @@ class PluginAU 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 @@ -1612,6 +1624,301 @@ class PluginAU notifyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); } + // ---------------------------------------------------------------------------------------------------------------- + + 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(&keyRef), + reinterpret_cast(&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(&keyRef), + reinterpret_cast(&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(&data)),); + DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(),); + + #if DISTRHO_PLUGIN_WANT_PROGRAMS + CFNumberRef programRef = nullptr; + if (CFDictionaryGetValueIfPresent(data, CFSTR("program"), reinterpret_cast(&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(&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(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(&keyRef), + reinterpret_cast(&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(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(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(¶msRef)) + && CFGetTypeID(paramsRef) == CFArrayGetTypeID()) + { + const CFIndex numParams = CFArrayGetCount(paramsRef); + char* symbol = nullptr; + CFIndex symbolLen = -1; + + for (CFIndex i=0; i(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(&keyRef), + reinterpret_cast(&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(std::realloc(symbol, symbolLen + 1)); + } + DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII)); + + for (uint32_t j=0; j(ptr)->updateState(key, value); + return static_cast(ptr)->updateState(key, newValue); } #endif @@ -1761,6 +2085,10 @@ struct AudioComponentPlugInInstance { return reinterpret_cast(MIDIEvent); case kMusicDeviceSysExSelect: return reinterpret_cast(SysEx); + #else + case kMusicDeviceMIDIEventSelect: + case kMusicDeviceSysExSelect: + return nullptr; #endif } diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp index 618ffe7b3..44838fed7 100644 --- a/distrho/src/DistrhoPluginCLAP.cpp +++ b/distrho/src/DistrhoPluginCLAP.cpp @@ -600,8 +600,8 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback // 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 @@ class PluginCLAP : ClapEventQueue // 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 @@ class PluginCLAP : ClapEventQueue 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 @@ class PluginCLAP : ClapEventQueue { 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 index 17ffd3d7d..727051da7 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -756,6 +756,7 @@ class PluginExporter fPlugin->setParameterValue(index, value); } + /* bool getParameterIndexForSymbol(const char* const symbol, uint32_t& index) { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, false); @@ -771,6 +772,7 @@ class PluginExporter return false; } + */ uint32_t getPortGroupCount() const noexcept { @@ -884,7 +886,7 @@ class PluginExporter } #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 @@ class PluginExporter 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 index 26608c8cd..ce5a2c27d 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1353,38 +1353,26 @@ class PluginLv2 // 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 index 66689bede..eedf0346f 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1286,22 +1286,11 @@ class PluginVst : public ParameterAndNotesHelper 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 index 5fd53af07..3c417c2ac 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1180,7 +1180,7 @@ class PluginVst3 // 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 @@ class PluginVst3 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 @@ class PluginVst3 // 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 @@ class PluginVst3 // 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 @@ class PluginVst3 // 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 index e628d992d..98fb86c19 100644 --- a/distrho/src/DistrhoUIAU.mm +++ b/distrho/src/DistrhoUIAU.mm @@ -73,28 +73,40 @@ { 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(CFArrayGetValueAtIndex(keysRef, i)); + DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(keyRef) == CFStringGetTypeID()); + + const CFIndex keyRefLen = CFStringGetLength(keyRef); + if (keyLen < keyRefLen) + { + keyLen = keyRefLen; + key = static_cast(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 @@ 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 @@ UIExporter fUI; + #if DISTRHO_PLUGIN_WANT_STATE + std::vector fStateKeys; + #endif + // ---------------------------------------------------------------------------------------------------------------- // Idle setup @@ -149,21 +177,54 @@ static void _idleCallback(CFRunLoopTimerRef, void* const info) // ---------------------------------------------------------------------------------------------------------------- // 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(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 @@ static void auPropertyChangedCallback(void* const userData, 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 @@ static void auPropertyChangedCallback(void* const userData, 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 @@ static void setParameterCallback(void* const ptr, const uint32_t rindex, const f #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(std::malloc(len_combined)); - DISTRHO_SAFE_ASSERT_RETURN(data != nullptr,); + const std::vector::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 @@ void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) { const uint8_t data[3] = { static_cast((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 @@ - (NSView*) uiViewForAudioUnit:(AudioUnit)component withSize:(NSSize)inPreferred 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 index 37b018409..eb95da88f 100644 --- a/examples/States/DistrhoPluginInfo.h +++ b/examples/States/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2024 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 @@ -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 index 3843faf55..8016c3e2a 100644 --- 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)