commit 9d09b5e15b314eba3b96fdd70171e3f1ad1f0072
parent 0a441e24b3b9d5ee4e84662e75eb6a8f53f9a346
Author: falkTX <falktx@falktx.com>
Date: Mon, 9 Mar 2020 19:10:03 +0000
Start implementation of state files; rework some code where sane
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
12 files changed, 529 insertions(+), 178 deletions(-)
diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -830,6 +830,13 @@ protected:
virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0;
#endif
+#if DISTRHO_PLUGIN_WANT_STATEFILES
+ /**
+ TODO API under construction
+ */
+ virtual bool isStateFile(uint32_t index) = 0;
+#endif
+
/* --------------------------------------------------------------------------------------------------------
* Internal data */
diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -98,7 +98,7 @@ public:
/**
setParameterValue.
-
+
Change a parameter value in the Plugin.
*/
void setParameterValue(uint32_t index, float value);
@@ -111,6 +111,19 @@ public:
void setState(const char* key, const char* value);
#endif
+#if DISTRHO_PLUGIN_WANT_STATEFILES
+ /**
+ Request a new file from the host, matching the properties of a state key.@n
+ This will use the native host file browser if available, otherwise a DPF built-in file browser is used.@n
+ Response will be sent asynchronously to stateChanged, with the matching key and the new file as the value.@n
+ It is not possible to know if the action was cancelled by the user.
+
+ @return Success if a file-browser was opened, otherwise false.
+ @note You cannot request more than one file at a time.
+ */
+ bool requestStateFile(const char* key);
+#endif
+
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
/**
sendNote.
diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h
@@ -77,6 +77,10 @@
# define DISTRHO_PLUGIN_WANT_STATE 0
#endif
+#ifndef DISTRHO_PLUGIN_WANT_STATEFILES
+# define DISTRHO_PLUGIN_WANT_STATEFILES 0
+#endif
+
#ifndef DISTRHO_PLUGIN_WANT_FULL_STATE
# define DISTRHO_PLUGIN_WANT_FULL_STATE 0
#endif
@@ -108,7 +112,7 @@
// Define DISTRHO_UI_URI if needed
#ifndef DISTRHO_UI_URI
-# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI"
+# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI"
#endif
// -----------------------------------------------------------------------
@@ -128,6 +132,14 @@
#endif
// -----------------------------------------------------------------------
+// Enable state if plugin wants state files
+
+#if DISTRHO_PLUGIN_WANT_STATEFILES && ! DISTRHO_PLUGIN_WANT_STATE
+# undef DISTRHO_PLUGIN_WANT_STATE
+# define DISTRHO_PLUGIN_WANT_STATE 1
+#endif
+
+// -----------------------------------------------------------------------
// Enable full state if plugin exports presets
#if DISTRHO_PLUGIN_WANT_PROGRAMS && DISTRHO_PLUGIN_WANT_STATE && ! DISTRHO_PLUGIN_WANT_FULL_STATE
diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp
@@ -486,6 +486,15 @@ public:
return fData->stateDefValues[index];
}
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ bool isStateFile(const uint32_t index) const
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, false);
+
+ return fPlugin->isStateFile(index);
+ }
+# endif
+
# if DISTRHO_PLUGIN_WANT_FULL_STATE
String getState(const char* key) const
{
diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -101,7 +101,15 @@ public:
PluginJack(jack_client_t* const client)
: fPlugin(this, writeMidiCallback),
#if DISTRHO_PLUGIN_HAS_UI
- fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()),
+ fUI(this, 0,
+ nullptr, // edit param
+ setParameterValueCallback,
+ setStateCallback,
+ nullptr, // send note
+ setSizeCallback,
+ nullptr, // file request
+ getDesktopScaleFactor(),
+ fPlugin.getInstancePointer()),
#endif
fClient(client)
{
diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -24,6 +24,7 @@
#include "lv2/midi.h"
#include "lv2/options.h"
#include "lv2/parameters.h"
+#include "lv2/patch.h"
#include "lv2/state.h"
#include "lv2/time.h"
#include "lv2/urid.h"
@@ -54,7 +55,8 @@
START_NAMESPACE_DISTRHO
-typedef std::map<const String, String> StringMap;
+typedef std::map<const String, String> StringToStringMap;
+typedef std::map<const LV2_URID, String> UridToStringMap;
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
static const writeMidiFunc writeMidiCallback = nullptr;
@@ -65,7 +67,10 @@ static const writeMidiFunc writeMidiCallback = nullptr;
class PluginLv2
{
public:
- PluginLv2(const double sampleRate, const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker, const bool usingNominal)
+ PluginLv2(const double sampleRate,
+ const LV2_URID_Map* const uridMap,
+ const LV2_Worker_Schedule* const worker,
+ const bool usingNominal)
: fPlugin(this, writeMidiCallback),
fUsingNominal(usingNominal),
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
@@ -127,6 +132,15 @@ public:
const String& dkey(fPlugin.getStateKey(i));
fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
+
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ if (fPlugin.isStateFile(i))
+ {
+ const String dpf_lv2_key(DISTRHO_PLUGIN_URI "#" + dkey);
+ const LV2_URID urid = uridMap->map(uridMap->handle, dpf_lv2_key.buffer());
+ fUridStateFileMap[urid] = dkey;
+ }
+# endif
}
}
else
@@ -521,16 +535,16 @@ public:
}
#endif
- // check for messages from UI
-#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI
+ // check for messages from UI or files
+#if DISTRHO_PLUGIN_WANT_STATE && (DISTRHO_PLUGIN_HAS_UI || DISTRHO_PLUGIN_WANT_STATEFILES)
LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event)
{
if (event == nullptr)
break;
- if (event->body.type == fURIDs.distrhoState && fWorker != nullptr)
+ if (event->body.type == fURIDs.dpfKeyValue)
{
- const void* const data((const void*)(event + 1));
+ const void* const data = (const void*)(event + 1);
// check if this is our special message
if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0)
@@ -538,12 +552,28 @@ public:
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
fNeededUiSends[i] = true;
}
- else
// no, send to DSP as usual
+ else if (fWorker != nullptr)
{
- fWorker->schedule_work(fWorker->handle, event->body.size, data);
+ fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body);
}
}
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ else if (event->body.type == fURIDs.atomObject && fWorker != nullptr)
+ {
+ const LV2_Atom_Object* const object = (const LV2_Atom_Object*)&event->body;
+
+ const LV2_Atom* property = nullptr;
+ const LV2_Atom* value = nullptr;
+ lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr);
+
+ if (property != nullptr && property->type == fURIDs.atomURID &&
+ value != nullptr && value->type == fURIDs.atomPath)
+ {
+ fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body);
+ }
+ }
+# endif
}
#endif
@@ -653,19 +683,19 @@ public:
if (! fNeededUiSends[i])
continue;
- const String& key = fPlugin.getStateKey(i);
+ const String& curKey(fPlugin.getStateKey(i));
- for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
+ for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
- const String& curKey = cit->first;
+ const String& key(cit->first);
if (curKey != key)
continue;
- const String& value = cit->second;
+ const String& value(cit->second);
// set msg size (key + value + separator + 2x null terminator)
- const size_t msgSize(key.length()+value.length()+3);
+ const size_t msgSize = key.length()+value.length()+3;
if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset)
{
@@ -673,21 +703,18 @@ public:
break;
}
- // reserve msg space
- // FIXME create a large enough buffer beforehand
- char msgBuf[msgSize];
- std::memset(msgBuf, 0, msgSize);
-
- // write key and value in atom bufer
- std::memcpy(msgBuf, key.buffer(), key.length()+1);
- std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1);
-
// put data
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset);
aev->time.frames = 0;
- aev->body.type = fURIDs.distrhoState;
+ aev->body.type = fURIDs.dpfKeyValue;
aev->body.size = msgSize;
- std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize);
+
+ uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body);
+ std::memset(msgBuf, 0, msgSize);
+
+ // write key and value in atom buffer
+ std::memcpy(msgBuf, key.buffer(), key.length()+1);
+ std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1);
fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize));
@@ -795,7 +822,7 @@ public:
# if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update state
- for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
+ for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
fStateMap[key] = fPlugin.getState(key);
@@ -811,22 +838,52 @@ public:
{
# if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update current state
- for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
+ for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
fStateMap[key] = fPlugin.getState(key);
}
# endif
- for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
+ String dpf_lv2_key;
+ LV2_URID urid;
+
+ for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
{
- const String& key = cit->first;
- const String& value = cit->second;
+ const String& curKey(fPlugin.getStateKey(i));
+
+ for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
+ {
+ const String& key(cit->first);
+
+ if (curKey != key)
+ continue;
+
+ const String& value(cit->second);
- const String urnKey(DISTRHO_PLUGIN_LV2_STATE_PREFIX + key);
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ if (fPlugin.isStateFile(i))
+ {
+ dpf_lv2_key = DISTRHO_PLUGIN_URI "#";
+ urid = fURIDs.atomPath;
+ }
+ else
+# endif
+ {
+ dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX;
+ urid = fURIDs.atomString;
+ }
- // some hosts need +1 for the null terminator, even though the type is string
- store(handle, fUridMap->map(fUridMap->handle, urnKey.buffer()), value.buffer(), value.length()+1, fURIDs.atomString, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
+ dpf_lv2_key += key;
+
+ // some hosts need +1 for the null terminator, even though the type is string
+ store(handle,
+ fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
+ value.buffer(),
+ value.length()+1,
+ urid,
+ LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
+ }
}
return LV2_STATE_SUCCESS;
@@ -837,23 +894,42 @@ public:
size_t size;
uint32_t type, flags;
+ String dpf_lv2_key;
+ LV2_URID urid;
+
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
{
const String& key(fPlugin.getStateKey(i));
- const String urnKey(DISTRHO_PLUGIN_LV2_STATE_PREFIX + key);
+
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ if (fPlugin.isStateFile(i))
+ {
+ dpf_lv2_key = DISTRHO_PLUGIN_URI "#";
+ urid = fURIDs.atomPath;
+ }
+ else
+# endif
+ {
+ dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX;
+ urid = fURIDs.atomString;
+ }
+
+ dpf_lv2_key += key;
size = 0;
type = 0;
flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
- const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, urnKey.buffer()), &size, &type, &flags);
+ const void* data = retrieve(handle,
+ fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
+ &size, &type, &flags);
if (data == nullptr || size == 0)
continue;
- DISTRHO_SAFE_ASSERT_CONTINUE(type == fURIDs.atomString);
+ DISTRHO_SAFE_ASSERT_CONTINUE(type == urid);
- const char* const value((const char*)data);
- const std::size_t length(std::strlen(value));
+ const char* const value = (const char*)data;
+ const std::size_t length = std::strlen(value);
DISTRHO_SAFE_ASSERT_CONTINUE(length == size || length+1 == size);
setState(key, value);
@@ -871,12 +947,55 @@ public:
LV2_Worker_Status lv2_work(const void* const data)
{
- const char* const key((const char*)data);
- const char* const value(key+std::strlen(key)+1);
+ const LV2_Atom* const eventBody = (const LV2_Atom*)data;
- setState(key, value);
+ if (eventBody->type == fURIDs.dpfKeyValue)
+ {
+ const char* const key = (const char*)(eventBody + 1);
+ const char* const value = key + (std::strlen(key) + 1U);
- return LV2_WORKER_SUCCESS;
+ setState(key, value);
+ return LV2_WORKER_SUCCESS;
+ }
+
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ if (eventBody->type == fURIDs.atomObject)
+ {
+ const LV2_Atom_Object* const object = (const LV2_Atom_Object*)eventBody;
+
+ const LV2_Atom* property = nullptr;
+ const LV2_Atom* value = nullptr;
+ lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr);
+ DISTRHO_SAFE_ASSERT_RETURN(property != nullptr, LV2_WORKER_ERR_UNKNOWN);
+ DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID, LV2_WORKER_ERR_UNKNOWN);
+ DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, LV2_WORKER_ERR_UNKNOWN);
+ DISTRHO_SAFE_ASSERT_RETURN(value->type == fURIDs.atomPath, LV2_WORKER_ERR_UNKNOWN);
+
+ const LV2_URID urid = ((const LV2_Atom_URID*)property)->body;
+ const char* const filename = (const char*)(value + 1);
+
+ String key;
+
+ try {
+ key = fUridStateFileMap[urid];
+ } DISTRHO_SAFE_EXCEPTION_RETURN("lv2_work fUridStateFileMap[urid]", LV2_WORKER_ERR_UNKNOWN);
+
+ setState(key, filename);
+
+ for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
+ {
+ if (fPlugin.getStateKey(i) == key)
+ {
+ fNeededUiSends[i] = true;
+ break;
+ }
+ }
+
+ return LV2_WORKER_SUCCESS;
+ }
+# endif
+
+ return LV2_WORKER_ERR_UNKNOWN;
}
LV2_Worker_Status lv2_work_response(uint32_t, const void*)
@@ -995,16 +1114,21 @@ private:
// LV2 URIDs
struct URIDs {
+ const LV2_URID_Map* _uridMap;
LV2_URID atomBlank;
LV2_URID atomObject;
LV2_URID atomDouble;
LV2_URID atomFloat;
LV2_URID atomInt;
LV2_URID atomLong;
+ LV2_URID atomPath;
LV2_URID atomSequence;
LV2_URID atomString;
- LV2_URID distrhoState;
+ LV2_URID atomURID;
+ LV2_URID dpfKeyValue;
LV2_URID midiEvent;
+ LV2_URID patchProperty;
+ LV2_URID patchValue;
LV2_URID timePosition;
LV2_URID timeBar;
LV2_URID timeBarBeat;
@@ -1016,25 +1140,35 @@ private:
LV2_URID timeSpeed;
URIDs(const LV2_URID_Map* const uridMap)
- : atomBlank(uridMap->map(uridMap->handle, LV2_ATOM__Blank)),
- atomObject(uridMap->map(uridMap->handle, LV2_ATOM__Object)),
- atomDouble(uridMap->map(uridMap->handle, LV2_ATOM__Double)),
- atomFloat(uridMap->map(uridMap->handle, LV2_ATOM__Float)),
- atomInt(uridMap->map(uridMap->handle, LV2_ATOM__Int)),
- atomLong(uridMap->map(uridMap->handle, LV2_ATOM__Long)),
- atomSequence(uridMap->map(uridMap->handle, LV2_ATOM__Sequence)),
- atomString(uridMap->map(uridMap->handle, LV2_ATOM__String)),
- distrhoState(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
- midiEvent(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)),
- timePosition(uridMap->map(uridMap->handle, LV2_TIME__Position)),
- timeBar(uridMap->map(uridMap->handle, LV2_TIME__bar)),
- timeBarBeat(uridMap->map(uridMap->handle, LV2_TIME__barBeat)),
- timeBeatUnit(uridMap->map(uridMap->handle, LV2_TIME__beatUnit)),
- timeBeatsPerBar(uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar)),
- timeBeatsPerMinute(uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute)),
- timeTicksPerBeat(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)),
- timeFrame(uridMap->map(uridMap->handle, LV2_TIME__frame)),
- timeSpeed(uridMap->map(uridMap->handle, LV2_TIME__speed)) {}
+ : _uridMap(uridMap),
+ atomBlank(map(LV2_ATOM__Blank)),
+ atomObject(map(LV2_ATOM__Object)),
+ atomDouble(map(LV2_ATOM__Double)),
+ atomFloat(map(LV2_ATOM__Float)),
+ atomInt(map(LV2_ATOM__Int)),
+ atomLong(map(LV2_ATOM__Long)),
+ atomPath(map(LV2_ATOM__Path)),
+ atomSequence(map(LV2_ATOM__Sequence)),
+ atomString(map(LV2_ATOM__String)),
+ atomURID(map(LV2_ATOM__URID)),
+ dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
+ midiEvent(map(LV2_MIDI__MidiEvent)),
+ patchProperty(map(LV2_PATCH__property)),
+ patchValue(map(LV2_PATCH__value)),
+ timePosition(map(LV2_TIME__Position)),
+ timeBar(map(LV2_TIME__bar)),
+ timeBarBeat(map(LV2_TIME__barBeat)),
+ timeBeatUnit(map(LV2_TIME__beatUnit)),
+ timeBeatsPerBar(map(LV2_TIME__beatsPerBar)),
+ timeBeatsPerMinute(map(LV2_TIME__beatsPerMinute)),
+ timeTicksPerBeat(map(LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)),
+ timeFrame(map(LV2_TIME__frame)),
+ timeSpeed(map(LV2_TIME__speed)) {}
+
+ inline LV2_URID map(const char* const uri) const
+ {
+ return _uridMap->map(_uridMap->handle, uri);
+ }
} fURIDs;
// LV2 features
@@ -1042,7 +1176,7 @@ private:
const LV2_Worker_Schedule* const fWorker;
#if DISTRHO_PLUGIN_WANT_STATE
- StringMap fStateMap;
+ StringToStringMap fStateMap;
bool* fNeededUiSends;
void setState(const char* const key, const char* const newValue)
@@ -1054,7 +1188,7 @@ private:
return;
// check if key already exists
- for (StringMap::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);
@@ -1067,6 +1201,10 @@ private:
d_stderr("Failed to find plugin state with key \"%s\"", key);
}
+
+# if DISTRHO_PLUGIN_WANT_STATEFILES
+ UridToStringMap fUridStateFileMap;
+# endif
#endif
void updateParameterOutputsAndTriggers()
diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -22,6 +22,7 @@
#include "lv2/instance-access.h"
#include "lv2/midi.h"
#include "lv2/options.h"
+#include "lv2/patch.h"
#include "lv2/port-props.h"
#include "lv2/presets.h"
#include "lv2/resize-port.h"
@@ -45,6 +46,10 @@
# error DISTRHO_PLUGIN_URI undefined!
#endif
+#ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX
+# define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:"
+#endif
+
#ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE
# define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048
#endif
@@ -140,6 +145,9 @@ static const char* const lv2ManifestUiOptionalFeatures[] =
"ui:resize",
"ui:touch",
#endif
+#if DISTRHO_PLUGIN_WANT_STATEFILES
+ "ui:requestValue",
+#endif
nullptr
};
@@ -220,8 +228,8 @@ void lv2_generate_ttl(const char* const basename)
d_lastBufferSize = 0;
d_lastSampleRate = 0.0;
- String pluginDLL(basename);
- String pluginTTL(pluginDLL + ".ttl");
+ const String pluginDLL(basename);
+ const String pluginTTL(pluginDLL + ".ttl");
#if DISTRHO_PLUGIN_HAS_UI
String pluginUI(pluginDLL);
@@ -317,26 +325,45 @@ void lv2_generate_ttl(const char* const basename)
// header
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT
- pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
+ pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
#endif
- pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
- pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
- pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
+ pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
+ pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
+ pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
#ifdef DISTRHO_PLUGIN_BRAND
- pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
+ pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
#endif
- pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n";
- pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
- pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
+ pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n";
+ pluginString += "@prefix patch: <" LV2_PATCH_PREFIX "> .\n";
+ pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
+ pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT
- pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n";
+ pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n";
#endif
#if DISTRHO_PLUGIN_HAS_UI
- pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
+ pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
#endif
- pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n";
+ pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n";
pluginString += "\n";
+#if DISTRHO_PLUGIN_WANT_STATEFILES
+ // define writable states as lv2 parameters
+ bool hasStateFiles = false;
+
+ for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i)
+ {
+ if (! plugin.isStateFile(i))
+ continue;
+
+ const String& key(plugin.getStateKey(i));
+ pluginString += "<" DISTRHO_PLUGIN_URI "#" + key + ">\n";
+ pluginString += " a lv2:Parameter ;\n";
+ pluginString += " rdfs:label \"" + key + "\" ;\n";
+ pluginString += " rdfs:range atom:Path .\n\n";
+ hasStateFiles = true;
+ }
+#endif
+
// plugin
pluginString += "<" DISTRHO_PLUGIN_URI ">\n";
#ifdef DISTRHO_PLUGIN_LV2_CATEGORY
@@ -353,6 +380,21 @@ void lv2_generate_ttl(const char* const basename)
addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4);
addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4);
+#if DISTRHO_PLUGIN_WANT_STATEFILES
+ if (hasStateFiles)
+ {
+ for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i)
+ {
+ if (! plugin.isStateFile(i))
+ continue;
+
+ const String& key(plugin.getStateKey(i));
+ pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n";
+ }
+ pluginString += "\n";
+ }
+#endif
+
// UI
#if DISTRHO_PLUGIN_HAS_UI
pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n";
@@ -582,33 +624,36 @@ void lv2_generate_ttl(const char* const basename)
// unit
const String& unit(plugin.getParameterUnit(i));
- if (! unit.isEmpty())
+ if (unit.isNotEmpty() && ! unit.contains(" "))
{
- if (unit == "db" || unit == "dB")
+ String lunit(unit);
+ lunit.toLower();
+
+ /**/ if (lunit == "db")
{
pluginString += " unit:unit unit:db ;\n";
}
- else if (unit == "hz" || unit == "Hz")
+ else if (lunit == "hz")
{
pluginString += " unit:unit unit:hz ;\n";
}
- else if (unit == "khz" || unit == "kHz")
+ else if (lunit == "khz")
{
pluginString += " unit:unit unit:khz ;\n";
}
- else if (unit == "mhz" || unit == "mHz")
+ else if (lunit == "mhz")
{
pluginString += " unit:unit unit:mhz ;\n";
}
- else if (unit == "ms")
+ else if (lunit == "ms")
{
pluginString += " unit:unit unit:ms ;\n";
}
- else if (unit == "s")
+ else if (lunit == "s")
{
pluginString += " unit:unit unit:s ;\n";
}
- else if (unit == "%")
+ else if (lunit == "%")
{
pluginString += " unit:unit unit:pc ;\n";
}
@@ -796,7 +841,7 @@ void lv2_generate_ttl(const char* const basename)
const String key = plugin.getStateKey(j);
const String value = plugin.getState(key);
- presetString += " <urn:distrho:" + key + ">";
+ presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">";
if (value.length() < 10)
presetString += " \"" + value + "\" ;\n";
diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -161,12 +161,24 @@ public:
class UIVst
{
public:
- UIVst(const audioMasterCallback audioMaster, AEffect* const effect, ParameterCheckHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId, const float scaleFactor)
+ UIVst(const audioMasterCallback audioMaster,
+ AEffect* const effect,
+ ParameterCheckHelper* const uiHelper,
+ PluginExporter* const plugin,
+ const intptr_t winId, const float scaleFactor)
: fAudioMaster(audioMaster),
fEffect(effect),
fUiHelper(uiHelper),
fPlugin(plugin),
- fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, plugin->getInstancePointer()),
+ fUI(this, winId,
+ editParameterCallback,
+ setParameterCallback,
+ setStateCallback,
+ sendNoteCallback,
+ setSizeCallback,
+ nullptr, // TODO file request
+ scaleFactor,
+ plugin->getInstancePointer()),
fShouldCaptureVstKeys(false)
{
// FIXME only needed for windows?
@@ -581,7 +593,9 @@ public:
// TODO
const float scaleFactor = 1.0f;
- UIExporter tmpUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, scaleFactor, fPlugin.getInstancePointer());
+ UIExporter tmpUI(nullptr, 0,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ scaleFactor, fPlugin.getInstancePointer());
fVstRect.right = tmpUI.getWidth();
fVstRect.bottom = tmpUI.getHeight();
tmpUI.quit();
diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -103,6 +103,13 @@ void UI::setState(const char* key, const char* value)
}
#endif
+#if DISTRHO_PLUGIN_WANT_STATEFILES
+bool UI::requestStateFile(const char* key)
+{
+ return pData->fileRequestCallback(key);
+}
+#endif
+
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
{
diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp
@@ -97,7 +97,7 @@ class UIDssi
{
public:
UIDssi(const OscData& oscData, const char* const uiTitle)
- : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback),
+ : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, nullptr),
fHostClosed(false),
fOscData(oscData)
{
diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -48,11 +48,12 @@ extern Window* d_lastUiWindow;
// -----------------------------------------------------------------------
// UI callbacks
-typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
-typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
-typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
-typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
-typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
+typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
+typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
+typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
+typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
+typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
+typedef bool (*fileRequestFunc) (void* ptr, const char* key);
// -----------------------------------------------------------------------
// UI private data
@@ -72,12 +73,13 @@ struct UI::PrivateData {
uint minHeight;
// Callbacks
- void* callbacksPtr;
- editParamFunc editParamCallbackFunc;
- setParamFunc setParamCallbackFunc;
- setStateFunc setStateCallbackFunc;
- sendNoteFunc sendNoteCallbackFunc;
- setSizeFunc setSizeCallbackFunc;
+ void* callbacksPtr;
+ editParamFunc editParamCallbackFunc;
+ setParamFunc setParamCallbackFunc;
+ setStateFunc setStateCallbackFunc;
+ sendNoteFunc sendNoteCallbackFunc;
+ setSizeFunc setSizeCallbackFunc;
+ fileRequestFunc fileRequestCallbackFunc;
PrivateData() noexcept
: sampleRate(d_lastUiSampleRate),
@@ -94,7 +96,8 @@ struct UI::PrivateData {
setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr),
- setSizeCallbackFunc(nullptr)
+ setSizeCallbackFunc(nullptr),
+ fileRequestCallbackFunc(nullptr)
{
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));
@@ -144,6 +147,16 @@ struct UI::PrivateData {
if (setSizeCallbackFunc != nullptr)
setSizeCallbackFunc(callbacksPtr, width, height);
}
+
+ bool fileRequestCallback(const char* key)
+ {
+ if (fileRequestCallbackFunc != nullptr)
+ return fileRequestCallbackFunc(callbacksPtr, key);
+
+ // TODO use old style DPF dialog here
+
+ return false;
+ }
};
// -----------------------------------------------------------------------
@@ -258,6 +271,7 @@ public:
const setStateFunc setStateCall,
const sendNoteFunc sendNoteCall,
const setSizeFunc setSizeCall,
+ const fileRequestFunc fileRequestCall,
const float scaleFactor = 1.0f,
void* const dspPtr = nullptr,
const char* const bundlePath = nullptr)
@@ -274,12 +288,13 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
- fData->callbacksPtr = callbacksPtr;
- fData->editParamCallbackFunc = editParamCall;
- fData->setParamCallbackFunc = setParamCall;
- fData->setStateCallbackFunc = setStateCall;
- fData->sendNoteCallbackFunc = sendNoteCall;
- fData->setSizeCallbackFunc = setSizeCall;
+ fData->callbacksPtr = callbacksPtr;
+ fData->editParamCallbackFunc = editParamCall;
+ fData->setParamCallbackFunc = setParamCall;
+ fData->setStateCallbackFunc = setStateCall;
+ fData->sendNoteCallbackFunc = sendNoteCall;
+ fData->setSizeCallbackFunc = setSizeCall;
+ fData->fileRequestCallbackFunc = fileRequestCall;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// unused
diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
+ * Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,12 +19,14 @@
#include "../extra/String.hpp"
#include "lv2/atom.h"
+#include "lv2/atom-forge.h"
#include "lv2/atom-util.h"
#include "lv2/data-access.h"
#include "lv2/instance-access.h"
#include "lv2/midi.h"
#include "lv2/options.h"
#include "lv2/parameters.h"
+#include "lv2/patch.h"
#include "lv2/ui.h"
#include "lv2/urid.h"
#include "lv2/lv2_kxstudio_properties.h"
@@ -47,22 +49,46 @@ static const sendNoteFunc sendNoteCallback = nullptr;
// -----------------------------------------------------------------------
+template <class LV2F>
+static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri)
+{
+ for (int i=0; features[i] != nullptr; ++i)
+ {
+ if (std::strcmp(features[i]->URI, uri) == 0)
+ return (const LV2F*)features[i]->data;
+ }
+
+ return nullptr;
+}
+
class UiLv2
{
public:
- UiLv2(const char* const bundlePath, const intptr_t winId,
- const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch,
- const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc,
- const float scaleFactor, LV2UI_Widget* const widget, void* const dspPtr)
- : fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, dspPtr, bundlePath),
+ UiLv2(const char* const bundlePath,
+ const intptr_t winId,
+ const LV2_Options_Option* options,
+ const LV2_URID_Map* const uridMap,
+ const LV2_Feature* const* const features,
+ const LV2UI_Controller controller,
+ const LV2UI_Write_Function writeFunc,
+ const float scaleFactor,
+ LV2UI_Widget* const widget,
+ void* const dspPtr)
+ : fUI(this, winId,
+ editParameterCallback,
+ setParameterCallback,
+ setStateCallback,
+ sendNoteCallback,
+ setSizeCallback,
+ fileRequestCallback,
+ scaleFactor, dspPtr, bundlePath),
fUridMap(uridMap),
- fUiResize(uiResz),
- fUiTouch(uiTouch),
+ fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
+ fUiResize(getLv2Feature<LV2UI_Resize>(features, LV2_UI__resize)),
+ fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
fController(controller),
fWriteFunction(writeFunc),
- fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)),
- fMidiEventURID(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)),
- fKeyValueURID(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
+ fURIDs(uridMap),
fWinIdWasNull(winId == 0)
{
if (fUiResize != nullptr && winId != 0)
@@ -82,8 +108,8 @@ public:
// if winId == 0 then options must not be null
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);
- const LV2_URID uridWindowTitle(uridMap->map(uridMap->handle, LV2_UI__windowTitle));
- const LV2_URID uridTransientWinId(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId));
+ const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
+ const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId);
bool hasTitle = false;
@@ -91,7 +117,7 @@ public:
{
if (options[i].key == uridTransientWinId)
{
- if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Long))
+ if (options[i].type == fURIDs.atomLong)
{
if (const int64_t transientWinId = *(const int64_t*)options[i].value)
fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId));
@@ -101,7 +127,7 @@ public:
}
else if (options[i].key == uridWindowTitle)
{
- if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__String))
+ if (options[i].type == fURIDs.atomString)
{
if (const char* const windowTitle = (const char*)options[i].value)
{
@@ -124,27 +150,32 @@ public:
{
if (format == 0)
{
- const uint32_t parameterOffset(fUI.getParameterOffset());
+ const uint32_t parameterOffset = fUI.getParameterOffset();
if (rindex < parameterOffset)
return;
DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)
- const float value(*(const float*)buffer);
+ const float value = *(const float*)buffer;
fUI.parameterChanged(rindex-parameterOffset, value);
}
#if DISTRHO_PLUGIN_WANT_STATE
- else if (format == fEventTransferURID)
+ else if (format == fURIDs.atomEventTransfer)
{
- const LV2_Atom* const atom((const LV2_Atom*)buffer);
-
- DISTRHO_SAFE_ASSERT_RETURN(atom->type == fKeyValueURID,);
+ const LV2_Atom* const atom = (const LV2_Atom*)buffer;
- const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom);
- const char* const value = key+(std::strlen(key)+1);
+ if (atom->type == fURIDs.dpfKeyValue)
+ {
+ const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom);
+ const char* const value = key+(std::strlen(key)+1);
- fUI.stateChanged(key, value);
+ fUI.stateChanged(key, value);
+ }
+ else
+ {
+ d_stdout("received atom not dpfKeyValue");
+ }
}
#endif
}
@@ -187,11 +218,11 @@ public:
{
for (int i=0; options[i].key != 0; ++i)
{
- if (options[i].key == fUridMap->map(fUridMap->handle, LV2_PARAMETERS__sampleRate))
+ if (options[i].key == fURIDs.paramSampleRate)
{
- if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Float))
+ if (options[i].type == fURIDs.atomFloat)
{
- const float sampleRate(*(const float*)options[i].value);
+ const float sampleRate = *(const float*)options[i].value;
fUI.setSampleRate(sampleRate);
continue;
}
@@ -211,7 +242,7 @@ public:
#if DISTRHO_PLUGIN_WANT_PROGRAMS
void lv2ui_select_program(const uint32_t bank, const uint32_t program)
{
- const uint32_t realProgram(bank * 128 + program);
+ const uint32_t realProgram = bank * 128 + program;
fUI.programLoaded(realProgram);
}
@@ -237,7 +268,7 @@ protected:
{
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
- const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS);
+ const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
// join key and value
String tmpStr;
@@ -248,23 +279,23 @@ protected:
tmpStr[std::strlen(key)] = '\0';
// set msg size (key + separator + value + null terminator)
- const size_t msgSize(tmpStr.length()+1);
+ const size_t msgSize = tmpStr.length() + 1U;
// reserve atom space
- const size_t atomSize(sizeof(LV2_Atom) + msgSize);
+ const size_t atomSize = sizeof(LV2_Atom) + msgSize;
char atomBuf[atomSize];
std::memset(atomBuf, 0, atomSize);
// set atom info
- LV2_Atom* const atom((LV2_Atom*)atomBuf);
+ LV2_Atom* const atom = (LV2_Atom*)atomBuf;
atom->size = msgSize;
- atom->type = fKeyValueURID;
+ atom->type = fURIDs.dpfKeyValue;
// set atom data
std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);
// send to DSP side
- fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom);
+ fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
}
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -275,7 +306,7 @@ protected:
if (channel > 0xF)
return;
- const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS);
+ const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
LV2_Atom_MidiEvent atomMidiEvent;
atomMidiEvent.atom.size = 3;
@@ -286,7 +317,8 @@ protected:
atomMidiEvent.data[2] = velocity;
// send to DSP side
- fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), fEventTransferURID, &atomMidiEvent);
+ fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
+ fURIDs.atomEventTransfer, &atomMidiEvent);
}
#endif
@@ -298,22 +330,68 @@ protected:
fUiResize->ui_resize(fUiResize->handle, width, height);
}
+ bool fileRequest(const char* const key)
+ {
+ d_stdout("UI file request %s %p", key, fUiRequestValue);
+
+ if (fUiRequestValue == nullptr)
+ return false;
+
+ String dpf_lv2_key(DISTRHO_PLUGIN_URI "#");
+ dpf_lv2_key += key;
+
+ const int r = fUiRequestValue->request(fUiRequestValue->handle,
+ fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
+ fURIDs.atomPath,
+ nullptr);
+
+ d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r);
+ return r == LV2UI_REQUEST_VALUE_SUCCESS;
+ }
+
private:
UIExporter fUI;
// LV2 features
- const LV2_URID_Map* const fUridMap;
- const LV2UI_Resize* const fUiResize;
- const LV2UI_Touch* const fUiTouch;
+ const LV2_URID_Map* const fUridMap;
+ const LV2UI_Request_Value* const fUiRequestValue;
+ const LV2UI_Resize* const fUiResize;
+ const LV2UI_Touch* const fUiTouch;
// LV2 UI stuff
const LV2UI_Controller fController;
const LV2UI_Write_Function fWriteFunction;
- // Need to save this
- const LV2_URID fEventTransferURID;
- const LV2_URID fMidiEventURID;
- const LV2_URID fKeyValueURID;
+ // LV2 URIDs
+ const struct URIDs {
+ const LV2_URID_Map* _uridMap;
+ LV2_URID dpfKeyValue;
+ LV2_URID atomEventTransfer;
+ LV2_URID atomFloat;
+ LV2_URID atomLong;
+ LV2_URID atomPath;
+ LV2_URID atomString;
+ LV2_URID midiEvent;
+ LV2_URID paramSampleRate;
+ LV2_URID patchSet;
+
+ URIDs(const LV2_URID_Map* const uridMap)
+ : _uridMap(uridMap),
+ dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
+ atomEventTransfer(map(LV2_ATOM__eventTransfer)),
+ atomFloat(map(LV2_ATOM__Float)),
+ atomLong(map(LV2_ATOM__Long)),
+ atomPath(map(LV2_ATOM__Path)),
+ atomString(map(LV2_ATOM__String)),
+ midiEvent(map(LV2_MIDI__MidiEvent)),
+ paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
+ patchSet(map(LV2_PATCH__Set)) {}
+
+ inline LV2_URID map(const char* const uri) const
+ {
+ return _uridMap->map(_uridMap->handle, uri);
+ }
+ } fURIDs;
// using ui:showInterface if true
bool fWinIdWasNull;
@@ -350,13 +428,23 @@ private:
uiPtr->setSize(width, height);
}
+ static bool fileRequestCallback(void* ptr, const char* key)
+ {
+ return uiPtr->fileRequest(key);
+ }
+
#undef uiPtr
};
// -----------------------------------------------------------------------
-static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char* bundlePath,
- LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
+static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
+ const char* const uri,
+ const char* const bundlePath,
+ const LV2UI_Write_Function writeFunction,
+ const LV2UI_Controller controller,
+ LV2UI_Widget* const widget,
+ const LV2_Feature* const* const features)
{
if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0)
{
@@ -364,12 +452,10 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
return nullptr;
}
- const LV2_Options_Option* options = nullptr;
- const LV2_URID_Map* uridMap = nullptr;
- const LV2UI_Resize* uiResize = nullptr;
- const LV2UI_Touch* uiTouch = nullptr;
- void* parentId = nullptr;
- void* instance = nullptr;
+ const LV2_Options_Option* options = nullptr;
+ const LV2_URID_Map* uridMap = nullptr;
+ void* parentId = nullptr;
+ void* instance = nullptr;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
struct LV2_DirectAccess_Interface {
@@ -380,16 +466,12 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
for (int i=0; features[i] != nullptr; ++i)
{
- if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
+ /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
options = (const LV2_Options_Option*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
uridMap = (const LV2_URID_Map*)features[i]->data;
- else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0)
- uiResize = (const LV2UI_Resize*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0)
parentId = features[i]->data;
- else if (std::strcmp(features[i]->URI, LV2_UI__touch) == 0)
- uiTouch = (const LV2UI_Touch*)features[i]->data;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
extData = (const LV2_Extension_Data_Feature*)features[i]->data;
@@ -439,9 +521,9 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
if (options != nullptr)
{
- const LV2_URID uridAtomFloat(uridMap->map(uridMap->handle, LV2_ATOM__Float));
- const LV2_URID uridSampleRate(uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate));
- const LV2_URID uridScaleFactor(uridMap->map(uridMap->handle, LV2_UI__scaleFactor));
+ const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
+ const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
+ const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
for (int i=0; options[i].key != 0; ++i)
{
@@ -468,7 +550,8 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri,
d_lastUiSampleRate = 44100.0;
}
- return new UiLv2(bundlePath, winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, scaleFactor, widget, instance);
+ return new UiLv2(bundlePath, winId, options, uridMap, features,
+ controller, writeFunction, scaleFactor, widget, instance);
}
#define uiPtr ((UiLv2*)ui)