DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

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:
Mdistrho/DistrhoPlugin.hpp | 9++++++++-
Mdistrho/DistrhoUI.hpp | 17+++++++++++++++--
Mdistrho/src/DistrhoPluginChecks.h | 14+++++++++++++-
Mdistrho/src/DistrhoPluginInternal.hpp | 9+++++++++
Mdistrho/src/DistrhoPluginJack.cpp | 12++++++++++--
Mdistrho/src/DistrhoPluginLV2.cpp | 266++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mdistrho/src/DistrhoPluginLV2export.cpp | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mdistrho/src/DistrhoPluginVST.cpp | 22++++++++++++++++++----
Mdistrho/src/DistrhoUI.cpp | 9++++++++-
Mdistrho/src/DistrhoUIDSSI.cpp | 2+-
Mdistrho/src/DistrhoUIInternal.hpp | 53++++++++++++++++++++++++++++++++++-------------------
Mdistrho/src/DistrhoUILV2.cpp | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
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)