diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 64a2fd74..9c3727aa 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -926,6 +926,15 @@ public: bool requestParameterValueChange(uint32_t index, float value) noexcept; #endif +#if DISTRHO_PLUGIN_WANT_STATE + /** + Notify the host about a state value change. + This function will automatically trigger a state update on the UI side. + It must not be called during run. + */ + bool updateStateValue(const char* key, const char* value) noexcept; +#endif + protected: /* -------------------------------------------------------------------------------------------------------- * Information */ diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 7ed9cf73..58db96aa 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -145,6 +145,13 @@ bool Plugin::requestParameterValueChange(const uint32_t index, const float value } #endif +#if DISTRHO_PLUGIN_WANT_STATE +bool Plugin::updateStateValue(const char* const key, const char* const value) noexcept +{ + return pData->updateStateValueCallback(key, value); +} +#endif + /* ------------------------------------------------------------------------------------------------------------ * Init */ diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 52185510..5a229d11 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -46,6 +46,7 @@ extern bool d_nextCanRequestParameterValueChanges; typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value); +typedef bool (*updateStateValueFunc) (void* ptr, const char* key, const char* value); // ----------------------------------------------------------------------- // Helpers @@ -127,6 +128,7 @@ struct Plugin::PrivateData { void* callbacksPtr; writeMidiFunc writeMidiCallbackFunc; requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc; + updateStateValueFunc updateStateValueCallbackFunc; uint32_t bufferSize; double sampleRate; @@ -159,6 +161,7 @@ struct Plugin::PrivateData { callbacksPtr(nullptr), writeMidiCallbackFunc(nullptr), requestParameterValueChangeCallbackFunc(nullptr), + updateStateValueCallbackFunc(nullptr), bufferSize(d_nextBufferSize), sampleRate(d_nextSampleRate), bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr) @@ -257,6 +260,17 @@ struct Plugin::PrivateData { return false; } #endif + +#if DISTRHO_PLUGIN_WANT_STATE + bool updateStateValueCallback(const char* const key, const char* const value) + { + d_stdout("updateStateValueCallback %p", updateStateValueCallbackFunc); + if (updateStateValueCallbackFunc != nullptr) + return updateStateValueCallbackFunc(callbacksPtr, key, value); + + return false; + } +#endif }; // ----------------------------------------------------------------------- @@ -267,7 +281,8 @@ class PluginExporter public: PluginExporter(void* const callbacksPtr, const writeMidiFunc writeMidiCall, - const requestParameterValueChangeFunc requestParameterValueChangeCall) + const requestParameterValueChangeFunc requestParameterValueChangeCall, + const updateStateValueFunc updateStateValueCall) : fPlugin(createPlugin()), fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), fIsActive(false) @@ -409,6 +424,7 @@ public: fData->callbacksPtr = callbacksPtr; fData->writeMidiCallbackFunc = writeMidiCall; fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall; + fData->updateStateValueCallbackFunc = updateStateValueCall; } ~PluginExporter() diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 2da88f12..1ba24631 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -62,6 +62,9 @@ static const writeMidiFunc writeMidiCallback = nullptr; #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif +#if ! DISTRHO_PLUGIN_WANT_STATE +static const updateStateValueFunc updateStateValueCallback = nullptr; +#endif // ----------------------------------------------------------------------- @@ -73,7 +76,7 @@ public: const LV2_Worker_Schedule* const worker, const LV2_ControlInputPort_Change_Request* const ctrlInPortChangeReq, const bool usingNominal) - : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), + : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), fUsingNominal(usingNominal), #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD fRunCount(0), @@ -727,7 +730,11 @@ public: if (hints & kStateIsHostVisible) { // object, prop key, prop urid, value key, value - msgSize = sizeof(LV2_Atom_Object) + sizeof(LV2_URID) * 3 + value.length() + 1; + msgSize = sizeof(LV2_Atom_Object) + + sizeof(LV2_Atom_Property_Body) * 4 + + sizeof(LV2_Atom_URID) * 3 + + sizeof(LV2_Atom_String) + + value.length() + 1; } else { @@ -737,7 +744,8 @@ public: if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) { - d_stdout("Sending key '%s' to UI failed, out of space", key.buffer()); + d_stdout("Sending key '%s' to UI failed, out of space (needs %u bytes)", + key.buffer(), msgSize); break; } @@ -745,10 +753,9 @@ public: aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); aev->time.frames = 0; - uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); - if (hints & kStateIsHostVisible) { + uint8_t* const msgBuf = (uint8_t*)&aev->body; LV2_Atom_Forge atomForge = fAtomForge; lv2_atom_forge_set_buffer(&atomForge, msgBuf, msgSize); @@ -759,7 +766,10 @@ public: lv2_atom_forge_urid(&atomForge, fUrids[i]); lv2_atom_forge_key(&atomForge, fURIDs.patchValue); - lv2_atom_forge_path(&atomForge, value.buffer(), static_cast(value.length()+1)); + if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) + lv2_atom_forge_path(&atomForge, value.buffer(), static_cast(value.length()+1)); + else + lv2_atom_forge_string(&atomForge, value.buffer(), static_cast(value.length()+1)); lv2_atom_forge_pop(&atomForge, &forgeFrame); } @@ -768,6 +778,7 @@ public: aev->body.type = fURIDs.dpfKeyValue; aev->body.size = msgSize; + uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); std::memset(msgBuf, 0, msgSize); // write key and value in atom buffer @@ -1248,11 +1259,14 @@ private: { fPlugin.setState(key, newValue); - // check if we want to save this key - if (! fPlugin.wantStateKey(key)) - return; + // save this key if necessary + if (fPlugin.wantStateKey(key)) + updateState(key, newValue, false); + } - // check if key already exists + bool updateState(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) { const String& dkey(it->first); @@ -1260,11 +1274,25 @@ private: if (dkey == key) { it->second = newValue; - return; + + if (sendToUI) + { + for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) + { + if (fPlugin.getStateKey(i) == key) + { + fNeededUiSends[i] = true; + break; + } + } + } + + return true; } } d_stderr("Failed to find plugin state with key \"%s\"", key); + return false; } #endif @@ -1306,6 +1334,13 @@ private: } #endif +#if DISTRHO_PLUGIN_WANT_STATE + static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) + { + return ((PluginLv2*)ptr)->updateState(key, value, true); + } +#endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT bool writeMidi(const MidiEvent& midiEvent) { diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 40abb09c..90fdca6b 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -244,7 +244,7 @@ void lv2_generate_ttl(const char* const basename) d_nextBufferSize = 512; d_nextSampleRate = 44100.0; d_nextPluginIsDummy = true; - PluginExporter plugin(nullptr, nullptr, nullptr); + PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); d_nextBufferSize = 0; d_nextSampleRate = 0.0; d_nextPluginIsDummy = false;