Browse Source

Revamp state API, make it a struct

Signed-off-by: falkTX <falktx@falktx.com>
pull/375/head
parent
commit
854a9c7649
9 changed files with 207 additions and 120 deletions
  1. +81
    -15
      distrho/DistrhoPlugin.hpp
  2. +30
    -31
      distrho/src/DistrhoPlugin.cpp
  3. +25
    -19
      distrho/src/DistrhoPluginInternal.hpp
  4. +11
    -7
      distrho/src/DistrhoPluginLV2.cpp
  5. +21
    -7
      distrho/src/DistrhoPluginLV2export.cpp
  6. +2
    -2
      distrho/src/DistrhoPluginVST2.cpp
  7. +2
    -2
      distrho/src/DistrhoPluginVST3.cpp
  8. +11
    -24
      examples/FileHandling/FileHandlingPlugin.cpp
  9. +24
    -13
      examples/States/ExamplePluginStates.cpp

+ 81
- 15
distrho/DistrhoPlugin.hpp View File

@@ -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;

/** @} */

@@ -639,6 +662,48 @@ struct PortGroup {
String symbol;
};

/**
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.
*/
@@ -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


+ 30
- 31
distrho/src/DistrhoPlugin.cpp View File

@@ -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



+ 25
- 19
distrho/src/DistrhoPluginInternal.hpp View File

@@ -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;
}



+ 11
- 7
distrho/src/DistrhoPluginLV2.cpp View File

@@ -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


+ 21
- 7
distrho/src/DistrhoPluginLV2export.cpp View File

@@ -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 + ">";



+ 2
- 2
distrho/src/DistrhoPluginVST2.cpp View File

@@ -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



+ 2
- 2
distrho/src/DistrhoPluginVST3.cpp View File

@@ -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



+ 11
- 24
examples/FileHandling/FileHandlingPlugin.cpp View File

@@ -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 = "";
}

/* --------------------------------------------------------------------------------------------------------


+ 24
- 13
examples/States/ExamplePluginStates.cpp View File

@@ -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";
}
/* --------------------------------------------------------------------------------------------------------


Loading…
Cancel
Save