Signed-off-by: falkTX <falktx@falktx.com>pull/375/head
@@ -926,6 +926,15 @@ public: | |||||
bool requestParameterValueChange(uint32_t index, float value) noexcept; | bool requestParameterValueChange(uint32_t index, float value) noexcept; | ||||
#endif | #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: | protected: | ||||
/* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
* Information */ | * Information */ | ||||
@@ -145,6 +145,13 @@ bool Plugin::requestParameterValueChange(const uint32_t index, const float value | |||||
} | } | ||||
#endif | #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 */ | * Init */ | ||||
@@ -46,6 +46,7 @@ extern bool d_nextCanRequestParameterValueChanges; | |||||
typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); | typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); | ||||
typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value); | typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value); | ||||
typedef bool (*updateStateValueFunc) (void* ptr, const char* key, const char* value); | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Helpers | // Helpers | ||||
@@ -127,6 +128,7 @@ struct Plugin::PrivateData { | |||||
void* callbacksPtr; | void* callbacksPtr; | ||||
writeMidiFunc writeMidiCallbackFunc; | writeMidiFunc writeMidiCallbackFunc; | ||||
requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc; | requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc; | ||||
updateStateValueFunc updateStateValueCallbackFunc; | |||||
uint32_t bufferSize; | uint32_t bufferSize; | ||||
double sampleRate; | double sampleRate; | ||||
@@ -159,6 +161,7 @@ struct Plugin::PrivateData { | |||||
callbacksPtr(nullptr), | callbacksPtr(nullptr), | ||||
writeMidiCallbackFunc(nullptr), | writeMidiCallbackFunc(nullptr), | ||||
requestParameterValueChangeCallbackFunc(nullptr), | requestParameterValueChangeCallbackFunc(nullptr), | ||||
updateStateValueCallbackFunc(nullptr), | |||||
bufferSize(d_nextBufferSize), | bufferSize(d_nextBufferSize), | ||||
sampleRate(d_nextSampleRate), | sampleRate(d_nextSampleRate), | ||||
bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr) | bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr) | ||||
@@ -257,6 +260,17 @@ struct Plugin::PrivateData { | |||||
return false; | return false; | ||||
} | } | ||||
#endif | #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: | public: | ||||
PluginExporter(void* const callbacksPtr, | PluginExporter(void* const callbacksPtr, | ||||
const writeMidiFunc writeMidiCall, | const writeMidiFunc writeMidiCall, | ||||
const requestParameterValueChangeFunc requestParameterValueChangeCall) | |||||
const requestParameterValueChangeFunc requestParameterValueChangeCall, | |||||
const updateStateValueFunc updateStateValueCall) | |||||
: fPlugin(createPlugin()), | : fPlugin(createPlugin()), | ||||
fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), | fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), | ||||
fIsActive(false) | fIsActive(false) | ||||
@@ -409,6 +424,7 @@ public: | |||||
fData->callbacksPtr = callbacksPtr; | fData->callbacksPtr = callbacksPtr; | ||||
fData->writeMidiCallbackFunc = writeMidiCall; | fData->writeMidiCallbackFunc = writeMidiCall; | ||||
fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall; | fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall; | ||||
fData->updateStateValueCallbackFunc = updateStateValueCall; | |||||
} | } | ||||
~PluginExporter() | ~PluginExporter() | ||||
@@ -62,6 +62,9 @@ static const writeMidiFunc writeMidiCallback = nullptr; | |||||
#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | ||||
static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; | static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; | ||||
#endif | #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_Worker_Schedule* const worker, | ||||
const LV2_ControlInputPort_Change_Request* const ctrlInPortChangeReq, | const LV2_ControlInputPort_Change_Request* const ctrlInPortChangeReq, | ||||
const bool usingNominal) | const bool usingNominal) | ||||
: fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), | |||||
: fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), | |||||
fUsingNominal(usingNominal), | fUsingNominal(usingNominal), | ||||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | ||||
fRunCount(0), | fRunCount(0), | ||||
@@ -727,7 +730,11 @@ public: | |||||
if (hints & kStateIsHostVisible) | if (hints & kStateIsHostVisible) | ||||
{ | { | ||||
// object, prop key, prop urid, value key, value | // 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 | else | ||||
{ | { | ||||
@@ -737,7 +744,8 @@ public: | |||||
if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) | 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; | break; | ||||
} | } | ||||
@@ -745,10 +753,9 @@ public: | |||||
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | ||||
aev->time.frames = 0; | aev->time.frames = 0; | ||||
uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); | |||||
if (hints & kStateIsHostVisible) | if (hints & kStateIsHostVisible) | ||||
{ | { | ||||
uint8_t* const msgBuf = (uint8_t*)&aev->body; | |||||
LV2_Atom_Forge atomForge = fAtomForge; | LV2_Atom_Forge atomForge = fAtomForge; | ||||
lv2_atom_forge_set_buffer(&atomForge, msgBuf, msgSize); | lv2_atom_forge_set_buffer(&atomForge, msgBuf, msgSize); | ||||
@@ -759,7 +766,10 @@ public: | |||||
lv2_atom_forge_urid(&atomForge, fUrids[i]); | lv2_atom_forge_urid(&atomForge, fUrids[i]); | ||||
lv2_atom_forge_key(&atomForge, fURIDs.patchValue); | lv2_atom_forge_key(&atomForge, fURIDs.patchValue); | ||||
lv2_atom_forge_path(&atomForge, value.buffer(), static_cast<uint32_t>(value.length()+1)); | |||||
if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) | |||||
lv2_atom_forge_path(&atomForge, value.buffer(), static_cast<uint32_t>(value.length()+1)); | |||||
else | |||||
lv2_atom_forge_string(&atomForge, value.buffer(), static_cast<uint32_t>(value.length()+1)); | |||||
lv2_atom_forge_pop(&atomForge, &forgeFrame); | lv2_atom_forge_pop(&atomForge, &forgeFrame); | ||||
} | } | ||||
@@ -768,6 +778,7 @@ public: | |||||
aev->body.type = fURIDs.dpfKeyValue; | aev->body.type = fURIDs.dpfKeyValue; | ||||
aev->body.size = msgSize; | aev->body.size = msgSize; | ||||
uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); | |||||
std::memset(msgBuf, 0, msgSize); | std::memset(msgBuf, 0, msgSize); | ||||
// write key and value in atom buffer | // write key and value in atom buffer | ||||
@@ -1248,11 +1259,14 @@ private: | |||||
{ | { | ||||
fPlugin.setState(key, newValue); | 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) | for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | ||||
{ | { | ||||
const String& dkey(it->first); | const String& dkey(it->first); | ||||
@@ -1260,11 +1274,25 @@ private: | |||||
if (dkey == key) | if (dkey == key) | ||||
{ | { | ||||
it->second = newValue; | 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); | d_stderr("Failed to find plugin state with key \"%s\"", key); | ||||
return false; | |||||
} | } | ||||
#endif | #endif | ||||
@@ -1306,6 +1334,13 @@ private: | |||||
} | } | ||||
#endif | #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 | #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | ||||
bool writeMidi(const MidiEvent& midiEvent) | bool writeMidi(const MidiEvent& midiEvent) | ||||
{ | { | ||||
@@ -244,7 +244,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
d_nextBufferSize = 512; | d_nextBufferSize = 512; | ||||
d_nextSampleRate = 44100.0; | d_nextSampleRate = 44100.0; | ||||
d_nextPluginIsDummy = true; | d_nextPluginIsDummy = true; | ||||
PluginExporter plugin(nullptr, nullptr, nullptr); | |||||
PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); | |||||
d_nextBufferSize = 0; | d_nextBufferSize = 0; | ||||
d_nextSampleRate = 0.0; | d_nextSampleRate = 0.0; | ||||
d_nextPluginIsDummy = false; | d_nextPluginIsDummy = false; | ||||