commit 854a9c76490b84141b1fe3f84f5cd6ceb6ac5885
parent 34e55a3be4b348f567502cdeac81459b08df54ef
Author: falkTX <falktx@falktx.com>
Date: Fri, 11 Feb 2022 01:03:22 +0000
Revamp state API, make it a struct
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
9 files changed, 207 insertions(+), 120 deletions(-)
diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp
@@ -142,19 +142,42 @@ static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean;
@defgroup StateHints State Hints
Various state hints.
- @see Plugin::getStateHints
+ @see State::hints
@{
*/
/**
- State is available for the host to see and change.
+ State is visible and readable by hosts that support string-type plugin parameters.
*/
-static const uint32_t kStateIsHostVisible = 0x01;
+static const uint32_t kStateIsHostReadable = 0x01;
/**
- State is a filename path.
+ State is writable by the host, allowing users to arbitrarily change the state.@n
+ For obvious reasons a writable state is also readable by the host.
*/
-static const uint32_t kStateIsFilenamePath = 0x02 | kStateIsHostVisible;
+static const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable;
+
+/**
+ State is a filename path instead of a regular string.@n
+ The readable and writable hints are required for filenames to work, and thus are automatically set.
+ */
+static const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable;
+
+/**
+ State is a base64 encoded string.
+ */
+static const uint32_t kStateIsBase64Blob = 0x08;
+
+/**
+ State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes.
+ */
+static const uint32_t kStateIsOnlyForDSP = 0x10;
+
+/**
+ State is for UI side only.@n
+ If the DSP and UI are separate and the UI is not available, this property won't be saved.
+ */
+static const uint32_t kStateIsOnlyForUI = 0x20;
/** @} */
@@ -640,6 +663,48 @@ struct PortGroup {
};
/**
+ State.
+
+ In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n
+ By default states are completely internal to the plugin and not visible by the host.@n
+ Flags can be set to allow hosts to see and/or change them.
+
+ TODO API under construction
+ */
+struct State {
+ /**
+ Hints describing this state.
+ @note Changing these hints can break compatibility with previously saved data.
+ @see StateHints
+ */
+ uint32_t hints;
+
+ /**
+ The key or "symbol" of this state.@n
+ A state key is a short restricted name used as a machine and human readable identifier.
+ @note State keys MUST be unique within a plugin instance.
+ TODO define rules for allowed characters, must be usable as URI non-encoded parameters
+ */
+ String key;
+
+ /**
+ The default value of this state.
+ */
+ String defaultValue;
+
+ /**
+ String representation of this state.
+ */
+ String label;
+
+ /**
+ An extensive description/comment about this state.
+ @note This value is optional and only used for LV2.
+ */
+ String description;
+};
+
+/**
MIDI event.
*/
struct MidiEvent {
@@ -928,9 +993,13 @@ public:
#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.
+ Notify the host about a state value change.@n
+ This function will automatically trigger a state update on the UI side.@n
+ It must not be called during run.@n
+ The state must be host readable.
+ @note this function does nothing on DSSI plugin format, as DSSI only supports UI->DSP messages.
+
+ TODO API under construction
*/
bool updateStateValue(const char* key, const char* value) noexcept;
#endif
@@ -1020,17 +1089,14 @@ protected:
#if DISTRHO_PLUGIN_WANT_STATE
/**
- Set the state key and default value of @a index.@n
+ Initialize the state @a index.@n
This function will be called once, shortly after the plugin is created.@n
Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled.
*/
- virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue);
+ virtual void initState(uint32_t index, State& state);
- /**
- TODO API under construction, should likely be put in a new State struct with key, name defValue and hints
- Returns StateHints mask.
- */
- virtual uint32_t getStateHints(uint32_t index);
+ DISTRHO_DEPRECATED_BY("getStateHints(uint32_t,State&)")
+ virtual void initState(uint32_t, String&, String&) {}
#endif
#if DISTRHO_PLUGIN_WANT_STATEFILES
diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp
@@ -72,9 +72,8 @@ Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCou
if (stateCount > 0)
{
#if DISTRHO_PLUGIN_WANT_STATE
- pData->stateCount = stateCount;
- pData->stateKeys = new String[stateCount];
- pData->stateDefValues = new String[stateCount];
+ pData->stateCount = stateCount;
+ pData->states = new State[stateCount];
#else
d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1");
DPF_ABORT
@@ -185,27 +184,11 @@ void Plugin::initProgramName(uint32_t, String&) {}
#endif
#if DISTRHO_PLUGIN_WANT_STATE
-void Plugin::initState(uint32_t, String&, String&) {}
-#endif
-
-/* ------------------------------------------------------------------------------------------------------------
- * Init */
-
-float Plugin::getParameterValue(uint32_t) const { return 0.0f; }
-void Plugin::setParameterValue(uint32_t, float) {}
-
-#if DISTRHO_PLUGIN_WANT_PROGRAMS
-void Plugin::loadProgram(uint32_t) {}
-#endif
-
-#if DISTRHO_PLUGIN_WANT_FULL_STATE
-String Plugin::getState(const char*) const { return String(); }
-#endif
-
-#if DISTRHO_PLUGIN_WANT_STATE
-uint32_t Plugin::getStateHints(const uint32_t index)
+void Plugin::initState(const uint32_t index, State& state)
{
- #if DISTRHO_PLUGIN_WANT_STATEFILES
+ uint hints = 0x0;
+ String stateKey, defaultStateValue;
+
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -213,23 +196,39 @@ uint32_t Plugin::getStateHints(const uint32_t index)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
+ initState(index, stateKey, defaultStateValue);
+ #if DISTRHO_PLUGIN_WANT_STATEFILES
if (isStateFile(index))
+ hints = kStateIsFilenamePath;
+ #endif
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif
- return kStateIsFilenamePath;
- #endif
- return 0x0;
-
- #if !DISTRHO_PLUGIN_WANT_STATEFILES
- // unused
- (void)index;
- #endif
+ state.hints = hints;
+ state.key = stateKey;
+ state.label = stateKey;
+ state.defaultValue = defaultStateValue;
}
+#endif
+
+/* ------------------------------------------------------------------------------------------------------------
+ * Init */
+
+float Plugin::getParameterValue(uint32_t) const { return 0.0f; }
+void Plugin::setParameterValue(uint32_t, float) {}
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+void Plugin::loadProgram(uint32_t) {}
+#endif
+
+#if DISTRHO_PLUGIN_WANT_FULL_STATE
+String Plugin::getState(const char*) const { return String(); }
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
void Plugin::setState(const char*, const char*) {}
#endif
diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp
@@ -112,8 +112,7 @@ struct Plugin::PrivateData {
#if DISTRHO_PLUGIN_WANT_STATE
uint32_t stateCount;
- String* stateKeys;
- String* stateDefValues;
+ State* states;
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
@@ -152,8 +151,7 @@ struct Plugin::PrivateData {
#endif
#if DISTRHO_PLUGIN_WANT_STATE
stateCount(0),
- stateKeys(nullptr),
- stateDefValues(nullptr),
+ states(nullptr),
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
latency(0),
@@ -221,16 +219,10 @@ struct Plugin::PrivateData {
#endif
#if DISTRHO_PLUGIN_WANT_STATE
- if (stateKeys != nullptr)
+ if (states != nullptr)
{
- delete[] stateKeys;
- stateKeys = nullptr;
- }
-
- if (stateDefValues != nullptr)
- {
- delete[] stateDefValues;
- stateDefValues = nullptr;
+ delete[] states;
+ states = nullptr;
}
#endif
@@ -418,7 +410,7 @@ public:
#if DISTRHO_PLUGIN_WANT_STATE
for (uint32_t i=0, count=fData->stateCount; i < count; ++i)
- fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]);
+ fPlugin->initState(i, fData->states[i]);
#endif
fData->callbacksPtr = callbacksPtr;
@@ -747,25 +739,39 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, 0x0);
- return fPlugin->getStateHints(index);
+ return fData->states[index].hints;
}
const String& getStateKey(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
- return fData->stateKeys[index];
+ return fData->states[index].key;
}
const String& getStateDefaultValue(const uint32_t index) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
- return fData->stateDefValues[index];
+ return fData->states[index].defaultValue;
+ }
+
+ const String& getStateLabel(const uint32_t index) const noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+ return fData->states[index].label;
+ }
+
+ const String& getStateDescription(const uint32_t index) const noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+ return fData->states[index].description;
}
# if DISTRHO_PLUGIN_WANT_FULL_STATE
- String getState(const char* key) const
+ String getStateValue(const char* const key) const
{
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString);
DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', sFallbackString);
@@ -790,7 +796,7 @@ public:
for (uint32_t i=0; i < fData->stateCount; ++i)
{
- if (fData->stateKeys[i] == key)
+ if (fData->states[i].key == key)
return true;
}
diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp
@@ -706,7 +706,7 @@ public:
const uint32_t hints = fPlugin.getStateHints(i);
#if ! DISTRHO_PLUGIN_HAS_UI
- if ((hints & kStateIsHostVisible) == 0x0)
+ if ((hints & kStateIsHostReadable) == 0x0)
{
fNeededUiSends[i] = false;
continue;
@@ -727,7 +727,7 @@ public:
// set msg size
uint32_t msgSize;
- if (hints & kStateIsHostVisible)
+ if (hints & kStateIsHostReadable)
{
// object, prop key, prop urid, value key, value
msgSize = sizeof(LV2_Atom_Object)
@@ -753,7 +753,7 @@ public:
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset);
aev->time.frames = 0;
- if (hints & kStateIsHostVisible)
+ if (hints & kStateIsHostReadable)
{
uint8_t* const msgBuf = (uint8_t*)&aev->body;
LV2_Atom_Forge atomForge = fAtomForge;
@@ -894,7 +894,7 @@ public:
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
- fStateMap[key] = fPlugin.getState(key);
+ fStateMap[key] = fPlugin.getStateValue(key);
}
# endif
}
@@ -910,7 +910,7 @@ public:
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
- fStateMap[key] = fPlugin.getState(key);
+ fStateMap[key] = fPlugin.getStateValue(key);
}
# endif
@@ -930,7 +930,9 @@ public:
const String& value(cit->second);
- if (const uint32_t hints = fPlugin.getStateHints(i))
+ const uint32_t hints = fPlugin.getStateHints(i);
+
+ if (hints & kStateIsHostReadable)
{
lv2key = DISTRHO_PLUGIN_URI "#";
urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath
@@ -970,7 +972,9 @@ public:
{
const String& key(fPlugin.getStateKey(i));
- if (const uint32_t hints = fPlugin.getStateHints(i))
+ const uint32_t hints = fPlugin.getStateHints(i);
+
+ if (hints & kStateIsHostReadable)
{
lv2key = DISTRHO_PLUGIN_URI "#";
urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath
diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp
@@ -376,13 +376,22 @@ void lv2_generate_ttl(const char* const basename)
{
const uint32_t hints = plugin.getStateHints(i);
- if ((hints & kStateIsHostVisible) == 0x0)
+ if ((hints & kStateIsHostReadable) == 0x0)
continue;
- const String& key(plugin.getStateKey(i));
- pluginString += "<" DISTRHO_PLUGIN_URI "#" + key + ">\n";
+ pluginString += "<" DISTRHO_PLUGIN_URI "#" + plugin.getStateKey(i) + ">\n";
pluginString += " a lv2:Parameter ;\n";
- pluginString += " rdfs:label \"" + key + "\" ;\n";
+ pluginString += " rdfs:label \"" + plugin.getStateLabel(i) + "\" ;\n";
+
+ const String& comment(plugin.getStateDescription(i));
+
+ if (comment.isNotEmpty())
+ {
+ if (comment.contains('"') || comment.contains('\n'))
+ pluginString += " rdfs:comment \"\"\"" + comment + "\"\"\" ;\n";
+ else
+ pluginString += " rdfs:comment \"" + comment + "\" ;\n";
+ }
if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath)
pluginString += " rdfs:range atom:Path .\n\n";
@@ -414,11 +423,16 @@ void lv2_generate_ttl(const char* const basename)
{
for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i)
{
- if ((plugin.getStateHints(i) & kStateIsHostVisible) == 0x0)
+ const uint32_t hints = plugin.getStateHints(i);
+
+ if ((hints & kStateIsHostReadable) == 0x0)
continue;
const String& key(plugin.getStateKey(i));
- pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n";
+ pluginString += " patch:readable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n";
+
+ if ((hints & kStateIsHostWritable) == kStateIsHostWritable)
+ pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n";
}
pluginString += "\n";
}
@@ -1274,7 +1288,7 @@ void lv2_generate_ttl(const char* const basename)
for (uint32_t j=0; j<numStates; ++j)
{
const String key = plugin.getStateKey(j);
- const String value = plugin.getState(key);
+ const String value = plugin.getStateValue(key);
presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">";
diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp
@@ -737,7 +737,7 @@ public:
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
- fStateMap[key] = fPlugin.getState(key);
+ fStateMap[key] = fPlugin.getStateValue(key);
}
# endif
@@ -813,7 +813,7 @@ public:
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
- fStateMap[key] = fPlugin.getState(key);
+ fStateMap[key] = fPlugin.getStateValue(key);
}
# endif
diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp
@@ -910,7 +910,7 @@ public:
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
- fStateMap[key] = fPlugin.getState(key);
+ fStateMap[key] = fPlugin.getStateValue(key);
}
#endif
@@ -1924,7 +1924,7 @@ public:
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
- fStateMap[key] = fPlugin.getState(key);
+ fStateMap[key] = fPlugin.getStateValue(key);
}
#endif
diff --git a/examples/FileHandling/FileHandlingPlugin.cpp b/examples/FileHandling/FileHandlingPlugin.cpp
@@ -124,42 +124,29 @@ protected:
}
/**
- Set the state key and default value of @a index.@n
- This function will be called once, shortly after the plugin is created.@n
- Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled.
+ Initialize the state @a index.@n
+ This function will be called once, shortly after the plugin is created.
*/
- void initState(uint32_t index, String& stateKey, String& defaultStateValue) override
+ void initState(uint32_t index, State& state) override
{
switch (index)
{
case kStateFile1:
- stateKey = "file1";
+ state.key = "file1";
+ state.label = "File 1";
break;
case kStateFile2:
- stateKey = "file2";
+ state.key = "file2";
+ state.label = "File 2";
break;
case kStateFile3:
- stateKey = "file3";
+ state.key = "file3";
+ state.label = "File 3";
break;
}
- defaultStateValue = "";
- }
-
- /**
- TODO API under construction
- */
- uint32_t getStateHints(uint32_t index) override
- {
- switch (index)
- {
- case kStateFile1:
- case kStateFile2:
- case kStateFile3:
- return kStateIsFilenamePath;
- }
-
- return 0x0;
+ state.hints = kStateIsFilenamePath;
+ state.defaultValue = "";
}
/* --------------------------------------------------------------------------------------------------------
diff --git a/examples/States/ExamplePluginStates.cpp b/examples/States/ExamplePluginStates.cpp
@@ -127,43 +127,54 @@ The plugin will be treated as an effect, but it will not change the host audio."
}
/**
- Set the state key and default value of @a index.
- This function will be called once, shortly after the plugin is created.
+ Initialize the state @a index.@n
+ This function will be called once, shortly after the plugin is created.@n
+ Must be implemented by your plugin class only if DISTRHO_PLUGIN_WANT_STATE is enabled.
*/
- void initState(uint32_t index, String& stateKey, String& defaultStateValue) override
+ void initState(uint32_t index, State& state) override
{
switch (index)
{
case 0:
- stateKey = "top-left";
+ state.key = "top-left";
+ state.label = "Top Left";
break;
case 1:
- stateKey = "top-center";
+ state.key = "top-center";
+ state.label = "Top Center";
break;
case 2:
- stateKey = "top-right";
+ state.key = "top-right";
+ state.label = "Top Right";
break;
case 3:
- stateKey = "middle-left";
+ state.key = "middle-left";
+ state.label = "Middle Left";
break;
case 4:
- stateKey = "middle-center";
+ state.key = "middle-center";
+ state.label = "Middle Center";
break;
case 5:
- stateKey = "middle-right";
+ state.key = "middle-right";
+ state.label = "Middle Right";
break;
case 6:
- stateKey = "bottom-left";
+ state.key = "bottom-left";
+ state.label = "Bottom Left";
break;
case 7:
- stateKey = "bottom-center";
+ state.key = "bottom-center";
+ state.label = "Bottom Center";
break;
case 8:
- stateKey = "bottom-right";
+ state.key = "bottom-right";
+ state.label = "Bottom Right";
break;
}
- defaultStateValue = "false";
+ state.hints = kStateIsHostWritable;
+ state.defaultValue = "false";
}
/* --------------------------------------------------------------------------------------------------------