| @@ -34,6 +34,7 @@ | |||
| #endif | |||
| #include <map> | |||
| #include <string> | |||
| #ifndef DISTRHO_PLUGIN_URI | |||
| # error DISTRHO_PLUGIN_URI undefined! | |||
| @@ -110,6 +111,20 @@ public: | |||
| #if DISTRHO_PLUGIN_WANT_LATENCY | |||
| fPortLatency = nullptr; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| if (const uint32_t count = fPlugin.getStateCount()) | |||
| { | |||
| fNeededUiSends = new bool[count]; | |||
| for (uint32_t i=0; i < count; ++i) | |||
| fNeededUiSends[i] = false; | |||
| } | |||
| else | |||
| { | |||
| fNeededUiSends = nullptr; | |||
| } | |||
| #endif | |||
| } | |||
| ~PluginLv2() | |||
| @@ -127,6 +142,12 @@ public: | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| if (fNeededUiSends != nullptr) | |||
| { | |||
| delete[] fNeededUiSends; | |||
| fNeededUiSends = nullptr; | |||
| } | |||
| fStateMap.clear(); | |||
| #endif | |||
| } | |||
| @@ -384,7 +405,18 @@ public: | |||
| if (event->body.type == fURIDs.distrhoState && fWorker != nullptr) | |||
| { | |||
| const void* const data((const void*)(event + 1)); | |||
| fWorker->schedule_work(fWorker->handle, event->body.size, data); | |||
| // check if this is our special message | |||
| if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) | |||
| { | |||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
| fNeededUiSends[i] = true; | |||
| } | |||
| else | |||
| // no, send to DSP as usual | |||
| { | |||
| fWorker->schedule_work(fWorker->handle, event->body.size, data); | |||
| } | |||
| continue; | |||
| } | |||
| @@ -404,10 +436,76 @@ public: | |||
| fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | |||
| #endif | |||
| updateParameterOutputs(); | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| #endif | |||
| const uint32_t capacity = fPortEventsOut->atom.size; | |||
| updateParameterOutputs(); | |||
| bool needsInit = true; | |||
| uint32_t size, offset = 0; | |||
| LV2_Atom_Event* aev; | |||
| for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
| { | |||
| if (! fNeededUiSends[i]) | |||
| continue; | |||
| const d_string& key = fPlugin.getStateKey(i); | |||
| for (auto it = fStateMap.begin(), end = fStateMap.end(); it != end; ++it) | |||
| { | |||
| const d_string& curKey = it->first; | |||
| if (curKey != key) | |||
| continue; | |||
| const d_string& value = it->second; | |||
| // TODO - RT safe | |||
| d_stdout("Got msg (from DSP to UI via host):\n%s\n%s", (const char*)key, (const char*)value); | |||
| // join key and value | |||
| std::string tmpStr; | |||
| tmpStr += std::string(key); | |||
| tmpStr += std::string("\0", 1); | |||
| tmpStr += std::string(value); | |||
| // get msg size | |||
| const size_t msgSize(tmpStr.size()+1); | |||
| if (sizeof(LV2_Atom_Event) + msgSize > capacity - offset) | |||
| return; | |||
| if (needsInit) | |||
| { | |||
| fPortEventsOut->atom.size = 0; | |||
| fPortEventsOut->atom.type = fURIDs.atomSequence; | |||
| fPortEventsOut->body.unit = 0; | |||
| fPortEventsOut->body.pad = 0; | |||
| needsInit = false; | |||
| } | |||
| // reserve atom space | |||
| const size_t atomSize(lv2_atom_pad_size(sizeof(LV2_Atom) + msgSize)); | |||
| char atomBuf[atomSize]; | |||
| std::memset(atomBuf, 0, atomSize); | |||
| // put data | |||
| aev = (LV2_Atom_Event*)((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fPortEventsOut) + offset); | |||
| aev->time.frames = 0; | |||
| aev->body.type = fURIDs.distrhoState; | |||
| aev->body.size = msgSize; | |||
| std::memcpy(LV2_ATOM_BODY(&aev->body), tmpStr.data(), msgSize-1); | |||
| size = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize); | |||
| offset += size; | |||
| fPortEventsOut->atom.size += size; | |||
| fNeededUiSends[i] = false; | |||
| break; | |||
| } | |||
| } | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -538,6 +636,13 @@ public: | |||
| continue; | |||
| setState(key, value); | |||
| d_stdout("Got state msg:\n%s\n%s", (const char*)key, value); | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| // signal msg needed for UI | |||
| fNeededUiSends[i] = true; | |||
| #endif | |||
| } | |||
| return LV2_STATE_SUCCESS; | |||
| @@ -610,6 +715,7 @@ private: | |||
| LV2_URID atomFloat; | |||
| LV2_URID atomInt; | |||
| LV2_URID atomLong; | |||
| LV2_URID atomSequence; | |||
| LV2_URID atomString; | |||
| LV2_URID distrhoState; | |||
| LV2_URID midiEvent; | |||
| @@ -629,6 +735,7 @@ private: | |||
| 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, "urn:distrho:keyValueState")), | |||
| midiEvent(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||
| @@ -650,6 +757,7 @@ private: | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| StringMap fStateMap; | |||
| bool* fNeededUiSends; | |||
| void setState(const char* const key, const char* const newValue) | |||
| { | |||
| @@ -22,6 +22,7 @@ | |||
| #include "lv2/instance-access.h" | |||
| #include "lv2/midi.h" | |||
| #include "lv2/options.h" | |||
| #include "lv2/resize-port.h" | |||
| #include "lv2/state.h" | |||
| #include "lv2/time.h" | |||
| #include "lv2/ui.h" | |||
| @@ -37,6 +38,10 @@ | |||
| # error DISTRHO_PLUGIN_URI undefined! | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | |||
| # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | |||
| #endif | |||
| #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
| #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||
| @@ -130,6 +135,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| 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 rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
| #endif | |||
| @@ -226,6 +232,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:name \"Events Input\" ;\n"; | |||
| pluginString += " lv2:symbol \"lv2_events_in\" ;\n"; | |||
| pluginString += " rsz:minimumSize " + d_string(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |||
| pluginString += " atom:bufferType atom:Sequence ;\n"; | |||
| # if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||
| pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||
| @@ -247,6 +254,7 @@ void lv2_generate_ttl(const char* const basename) | |||
| pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; | |||
| pluginString += " lv2:name \"Events Output\" ;\n"; | |||
| pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; | |||
| pluginString += " rsz:minimumSize " + d_string(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |||
| pluginString += " atom:bufferType atom:Sequence ;\n"; | |||
| pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||
| pluginString += " ] ;\n\n"; | |||
| @@ -40,9 +40,16 @@ public: | |||
| fUiResize(uiResz), | |||
| fUiTouch(uiTouch), | |||
| fController(controller), | |||
| fWriteFunction(writeFunc) | |||
| fWriteFunction(writeFunc), | |||
| fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | |||
| fKeyValueURID(uridMap->map(uridMap->handle, "urn:distrho:keyValueState")) | |||
| { | |||
| fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| // tell the DSP we're ready to receive msgs | |||
| setState("__dpf_ui_data__", ""); | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -61,10 +68,17 @@ public: | |||
| const float value(*(const float*)buffer); | |||
| fUI.parameterChanged(rindex-parameterOffset, value); | |||
| } | |||
| else | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| else if (format == fEventTransferURID) | |||
| { | |||
| //fUI.stateChanged(key, value); | |||
| const LV2_Atom* const atom((const LV2_Atom*)buffer); | |||
| const char* const stateKey((const char*)LV2_ATOM_BODY_CONST(atom)); | |||
| const char* const stateValue(stateKey+std::strlen(stateKey)+1); | |||
| d_stdout("Got MSG in UI from DSP ==> %s | %s", stateKey, stateValue); | |||
| fUI.stateChanged(stateKey, stateValue); | |||
| } | |||
| #endif | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -125,13 +139,13 @@ protected: | |||
| // set atom info | |||
| LV2_Atom* const atom((LV2_Atom*)atomBuf); | |||
| atom->size = msgSize; | |||
| atom->type = fUridMap->map(fUridMap->handle, "urn:distrho:keyValueState"); | |||
| atom->type = fKeyValueURID; | |||
| // set atom data | |||
| std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.data(), msgSize-1); | |||
| // send to DSP side | |||
| fWriteFunction(fController, eventInPortIndex, atomSize, fUridMap->map(fUridMap->handle, LV2_ATOM__eventTransfer), atom); | |||
| fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | |||
| } | |||
| void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) | |||
| @@ -156,6 +170,10 @@ private: | |||
| const LV2UI_Controller fController; | |||
| const LV2UI_Write_Function fWriteFunction; | |||
| // Need to save this | |||
| const LV2_URID fEventTransferURID; | |||
| const LV2_URID fKeyValueURID; | |||
| // ------------------------------------------------------------------- | |||
| // Callbacks | |||