diff --git a/distrho/DistrhoDetails.hpp b/distrho/DistrhoDetails.hpp index f72608f7..34affacb 100644 --- a/distrho/DistrhoDetails.hpp +++ b/distrho/DistrhoDetails.hpp @@ -202,7 +202,7 @@ static constexpr const uint32_t kStateIsOnlyForUI = 0x20; /** Parameter designation.@n - Allows a parameter to be specially designated for a task, like bypass. + Allows a parameter to be specially designated for a task, like bypass and reset. Each designation is unique, there must be only one parameter that uses it.@n The use of designated parameters is completely optional. @@ -214,13 +214,20 @@ enum ParameterDesignation { /** Null or unset designation. */ - kParameterDesignationNull = 0, + kParameterDesignationNull, /** Bypass designation.@n When on (> 0.5f), it means the plugin must run in a bypassed state. */ - kParameterDesignationBypass = 1 + kParameterDesignationBypass, + + /** + Reset designation.@n + When on (> 0.5f), it means the plugin should reset its internal processing state + (like filters, oscillators, envelopes, lfos, etc) and kill all voices. + */ + kParameterDesignationReset, }; /** @@ -234,7 +241,12 @@ namespace ParameterDesignationSymbols { static constexpr const char bypass[] = "dpf_bypass"; /** - Bypass designation symbol, inverted for LV2 so it becomes "enabled". + Reset designation symbol. + */ + static constexpr const char reset[] = "dpf_reset"; + + /** + LV2 bypass designation symbol, inverted for LV2 so it becomes "enabled". */ static constexpr const char bypass_lv2[] = "lv2_enabled"; }; @@ -728,6 +740,18 @@ struct Parameter { ranges.min = 0.0f; ranges.max = 1.0f; break; + case kParameterDesignationReset: + hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger|kParameterIsTrigger; + name = "Reset"; + shortName = "Reset"; + symbol = ParameterDesignationSymbols::reset; + unit = ""; + midiCC = 0; + groupId = kPortGroupNone; + ranges.def = 0.0f; + ranges.min = 0.0f; + ranges.max = 1.0f; + break; } } diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index c6ac59e5..b97bdb0d 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -264,7 +264,7 @@ bool isNumChannelsComboValid(const uint16_t numInputs, const uint16_t numOutputs // -------------------------------------------------------------------------------------------------------------------- struct PropertyListener { - AudioUnitPropertyID prop; + AudioUnitPropertyID prop; AudioUnitPropertyListenerProc proc; void* userData; }; @@ -348,7 +348,8 @@ public: fUsingRenderListeners(false), fParameterCount(fPlugin.getParameterCount()), fLastParameterValues(nullptr), - fBypassParameterIndex(UINT32_MAX) + fBypassParameterIndex(UINT32_MAX), + fResetParameterIndex(UINT32_MAX) #if DISTRHO_PLUGIN_WANT_MIDI_INPUT , fMidiEventCount(0) #endif @@ -365,7 +366,7 @@ public: , fStateCount(fPlugin.getStateCount()) #endif { - if (fParameterCount != 0) + if (fParameterCount != 0) { fLastParameterValues = new float[fParameterCount]; std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount); @@ -374,8 +375,17 @@ public: { fLastParameterValues[i] = fPlugin.getParameterValue(i); - if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass) + switch (fPlugin.getParameterDesignation(i)) + { + case kParameterDesignationNull: + break; + case kParameterDesignationBypass: fBypassParameterIndex = i; + break; + case kParameterDesignationReset: + fResetParameterIndex = i; + break; + } } } @@ -923,13 +933,13 @@ public: case kAudioUnitProperty_FastDispatch: switch (inElement) { - case kAudioUnitGetParameterSelect: + case kAudioUnitGetParameterSelect: *static_cast(outData) = FastDispatchGetParameter; return noErr; - case kAudioUnitSetParameterSelect: + case kAudioUnitSetParameterSelect: *static_cast(outData) = FastDispatchSetParameter; return noErr; - case kAudioUnitRenderSelect: + case kAudioUnitRenderSelect: *static_cast(outData) = FastDispatchRender; return noErr; } @@ -1458,7 +1468,7 @@ public: const float value = bypass ? 1.f : 0.f; fLastParameterValues[fBypassParameterIndex] = value; fPlugin.setParameterValue(fBypassParameterIndex, value); - notifyPropertyListeners(inProp, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); } } return noErr; @@ -1480,12 +1490,12 @@ public: #if DISTRHO_PLUGIN_WANT_TIMEPOS { const UInt32 usableDataSize = std::min(inDataSize, static_cast(sizeof(HostCallbackInfo))); - const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0; + const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0; - std::memcpy(&fHostCallbackInfo, inData, usableDataSize); + std::memcpy(&fHostCallbackInfo, inData, usableDataSize); if (sizeof(HostCallbackInfo) > usableDataSize) - std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize); + std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize); if (changed) notifyPropertyListeners(inProp, inScope, inElement); @@ -1612,7 +1622,7 @@ public: AUEventListenerNotify(NULL, NULL, &event); if (fBypassParameterIndex == inElement) - notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); + notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; @@ -1821,7 +1831,12 @@ public: DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); - if (fPlugin.isActive()) + if (fResetParameterIndex != UINT32_MAX) + { + fPlugin.setParameterValue(fResetParameterIndex, 1.f); + fPlugin.setParameterValue(fResetParameterIndex, 0.f); + } + else if (fPlugin.isActive()) { fPlugin.deactivate(); fPlugin.activate(); @@ -2180,6 +2195,7 @@ private: const uint32_t fParameterCount; float* fLastParameterValues; uint32_t fBypassParameterIndex; + uint32_t fResetParameterIndex; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t fMidiEventCount; @@ -2220,7 +2236,7 @@ private: const PropertyListener& pl(*it); if (pl.prop == prop) - pl.proc(pl.userData, fComponent, prop, scope, elem); + pl.proc(pl.userData, fComponent, prop, scope, elem); } } @@ -2845,7 +2861,7 @@ private: // -------------------------------------------------------------------------------------------------------------------- struct AudioComponentPlugInInstance { - AudioComponentPlugInInterface acpi; + AudioComponentPlugInInterface acpi; PluginAU* plugin; AudioComponentPlugInInstance() noexcept @@ -2854,9 +2870,9 @@ struct AudioComponentPlugInInstance { { std::memset(&acpi, 0, sizeof(acpi)); acpi.Open = Open; - acpi.Close = Close; - acpi.Lookup = Lookup; - acpi.reserved = nullptr; + acpi.Close = Close; + acpi.Lookup = Lookup; + acpi.reserved = nullptr; } ~AudioComponentPlugInInstance() @@ -2864,7 +2880,7 @@ struct AudioComponentPlugInInstance { delete plugin; } - static OSStatus Open(void* const self, const AudioUnit component) + static OSStatus Open(void* const self, const AudioUnit component) { d_debug("AudioComponentPlugInInstance::Open(%p)", self); @@ -2872,7 +2888,7 @@ struct AudioComponentPlugInInstance { return noErr; } - static OSStatus Close(void* const self) + static OSStatus Close(void* const self) { d_debug("AudioComponentPlugInInstance::Close(%p)", self); @@ -2964,15 +2980,15 @@ struct AudioComponentPlugInInstance { d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %d:%x:%s, %d:%s, %d, ...)", self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); - UInt32 dataSize = 0; - Boolean writable = false; + UInt32 dataSize = 0; + Boolean writable = false; const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable); - if (outDataSize != nullptr) - *outDataSize = dataSize; + if (outDataSize != nullptr) + *outDataSize = dataSize; - if (outWritable != nullptr) - *outWritable = writable; + if (outWritable != nullptr) + *outWritable = writable; return res; } @@ -3016,24 +3032,24 @@ struct AudioComponentPlugInInstance { if (res != noErr) return res; - void* outBuffer; + void* outBuffer; uint8_t* tmpBuffer; if (inDataSize < outDataSize) - { - tmpBuffer = new uint8_t[outDataSize]; - outBuffer = tmpBuffer; - } + { + tmpBuffer = new uint8_t[outDataSize]; + outBuffer = tmpBuffer; + } else { - tmpBuffer = nullptr; - outBuffer = outData; - } + tmpBuffer = nullptr; + outBuffer = outData; + } res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer); - if (res != noErr) + if (res != noErr) { - *ioDataSize = 0; + *ioDataSize = 0; return res; } diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp index b1487a9e..68fb9e4c 100644 --- a/distrho/src/DistrhoPluginCLAP.cpp +++ b/distrho/src/DistrhoPluginCLAP.cpp @@ -751,6 +751,7 @@ public: updateStateValueCallback), fHost(host), fOutputEvents(nullptr), + fResetParameterIndex(UINT32_MAX), #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 fUsingCV(false), #endif @@ -763,7 +764,19 @@ public: #endif fHostExtensions(host) { - fCachedParameters.setup(fPlugin.getParameterCount()); + if (const uint32_t paramCount = fPlugin.getParameterCount()) + { + fCachedParameters.setup(paramCount); + + for (uint32_t i=0; irequest_restart(fHost); + if (fResetParameterIndex != UINT32_MAX) + { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fMidiEventCount = 0; + #endif + fPlugin.setParameterValue(fResetParameterIndex, 1.f); + fPlugin.setParameterValue(fResetParameterIndex, 0.f); + } + else + { + fHost->request_restart(fHost); + } } bool process(const clap_process_t* const process) @@ -1119,14 +1143,19 @@ public: { const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); - if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass) + switch (fPlugin.getParameterDesignation(index)) { + case kParameterDesignationBypass: info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE; std::strcpy(info->name, "Bypass"); std::strcpy(info->module, "dpf_bypass"); - } - else - { + break; + case kParameterDesignationReset: + info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_READONLY; + std::strcpy(info->name, "Reset"); + std::strcpy(info->module, "dpf_reset"); + break; + default: const uint32_t hints = fPlugin.getParameterHints(index); const uint32_t groupId = fPlugin.getParameterGroupId(index); @@ -1156,6 +1185,7 @@ public: } d_strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn); + break; } info->id = index; @@ -1791,6 +1821,7 @@ private: const clap_host_t* const fHost; const clap_output_events_t* fOutputEvents; + uint32_t fResetParameterIndex; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 const float* fAudioInputs[DISTRHO_PLUGIN_NUM_INPUTS]; #endif diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index ba3cb196..3b0d22cb 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -758,6 +758,16 @@ void lv2_generate_ttl(const char* const basename) pluginString += " lv2:portProperty lv2:toggled , lv2:integer ;\n"; pluginString += " lv2:designation lv2:enabled ;\n"; break; + case kParameterDesignationReset: + designated = true; + pluginString += " lv2:name \"Reset\" ;\n"; + pluginString += " lv2:symbol \"" + String(ParameterDesignationSymbols::reset) + "\" ;\n"; + pluginString += " lv2:default 0 ;\n"; + pluginString += " lv2:minimum 0 ;\n"; + pluginString += " lv2:maximum 1 ;\n"; + pluginString += " lv2:portProperty lv2:toggled , lv2:integer , <" LV2_PORT_PROPS__trigger "> ;\n"; + pluginString += " lv2:designation <" LV2_KXSTUDIO_PROPERTIES__Reset "> ;\n"; + break; } } diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index cb743923..7295c892 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1747,6 +1747,12 @@ public: case kParameterDesignationBypass: flags |= V3_PARAM_IS_BYPASS; break; + case kParameterDesignationReset: + info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN; + info->step_count = 1; + strncpy_utf16(info->title, "Reset", 128); + strncpy_utf16(info->short_title, "Reset", 128); + return V3_OK; } if (hints & kParameterIsOutput) diff --git a/distrho/src/lv2/lv2_kxstudio_properties.h b/distrho/src/lv2/lv2_kxstudio_properties.h index 22922650..68dbe66f 100644 --- a/distrho/src/lv2/lv2_kxstudio_properties.h +++ b/distrho/src/lv2/lv2_kxstudio_properties.h @@ -1,6 +1,6 @@ /* LV2 KXStudio Properties Extension - Copyright 2014-2021 Filipe Coelho + Copyright 2014-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 @@ -27,6 +27,7 @@ #define LV2_KXSTUDIO_PROPERTIES_PREFIX LV2_KXSTUDIO_PROPERTIES_URI "#" #define LV2_KXSTUDIO_PROPERTIES__NonAutomatable LV2_KXSTUDIO_PROPERTIES_PREFIX "NonAutomatable" +#define LV2_KXSTUDIO_PROPERTIES__Reset LV2_KXSTUDIO_PROPERTIES_PREFIX "Reset" #define LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat LV2_KXSTUDIO_PROPERTIES_PREFIX "TimePositionTicksPerBeat" #define LV2_KXSTUDIO_PROPERTIES__TransientWindowId LV2_KXSTUDIO_PROPERTIES_PREFIX "TransientWindowId"