From efd2441fa1c01fe465c31033f4a4f5e78c656ff5 Mon Sep 17 00:00:00 2001 From: Filipe Coelho Date: Thu, 31 Jan 2019 23:14:27 +0100 Subject: [PATCH] Initial support for LV2 params (#811) * Quick & dirty LV2 params now working, testing waters... * Support LV2 UIs purely through show interface without complaining * Small tweak to how DSSI UIs are found, to be more inclusive * Make water File paths accept paths from CWD, adjust bridges to it * Add "plugin-wine" make target in cross-compile mingw mode * Whitespace * Bump maximum value of LFO speed * Rename a variable * Fix build * Always copy carla-plugin binary when exporting lv2 plugin * Fix typo * Do not build external plugins in DEBUG mode They make bigger binaries, take longer to build and sometimes even fail. We do not need them in DEBUG mode, since they are assumed to be tested and work well * Cleanup some water * Fix leaks and oddities with water Array class * Make ScopedLocale its own class, apply it everywhere that it fits * Cleanup * More cleanup, make lv2 params code not crash carla * Fake lv2 plugin gui for those with file paths, using first prop Signed-off-by: falkTX --- source/backend/plugin/CarlaPluginLV2.cpp | 232 +++++++++++++++----- source/includes/lv2_rdf.hpp | 19 +- source/utils/CarlaLv2Utils.hpp | 257 ++++++++++++++++++----- 3 files changed, 401 insertions(+), 107 deletions(-) diff --git a/source/backend/plugin/CarlaPluginLV2.cpp b/source/backend/plugin/CarlaPluginLV2.cpp index 50ae06027..564ec9ec7 100644 --- a/source/backend/plugin/CarlaPluginLV2.cpp +++ b/source/backend/plugin/CarlaPluginLV2.cpp @@ -114,6 +114,9 @@ enum CarlaLv2URIDs { kUridLogNote, kUridLogTrace, kUridLogWarning, + kUridPatchSet, + kUridPatchPoperty, + kUridPatchValue, // time base type kUridTimePosition, // time values @@ -527,7 +530,7 @@ public: fNeedsUiClose(false), fLatencyIndex(-1), fStrictBounds(-1), - fAtomBufferUiIn(), + fAtomBufferEvIn(), fAtomBufferUiOut(), fAtomBufferWorkerIn(), fAtomBufferWorkerResp(), @@ -542,6 +545,7 @@ public: fFirstActive(true), fLastStateChunk(nullptr), fLastTimeInfo(), + fFilePathURI(), fExt(), fUI() { @@ -1001,23 +1005,35 @@ public: CARLA_SAFE_ASSERT_RETURN(fRdfDescriptor != nullptr,); CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,); - // TODO param support + LV2_RDF_PortUnit* portUnit = nullptr; - const int32_t rindex(pData->param.data[parameterId].rindex); + int32_t rindex = pData->param.data[parameterId].rindex; if (rindex < static_cast(fRdfDescriptor->PortCount)) { - const LV2_RDF_Port* const port(&fRdfDescriptor->Ports[rindex]); + portUnit = &fRdfDescriptor->Ports[rindex].Unit; + } + else + { + rindex -= fRdfDescriptor->PortCount; + + if (rindex < static_cast(fRdfDescriptor->ParameterCount)) + { + portUnit = &fRdfDescriptor->Parameters[rindex].Unit; + } + } - if (LV2_HAVE_PORT_UNIT_SYMBOL(port->Unit.Hints) && port->Unit.Symbol != nullptr) + if (portUnit != nullptr) + { + if (LV2_HAVE_PORT_UNIT_SYMBOL(portUnit->Hints) && portUnit->Symbol != nullptr) { - std::strncpy(strBuf, port->Unit.Symbol, STR_MAX); + std::strncpy(strBuf, portUnit->Symbol, STR_MAX); return; } - if (LV2_HAVE_PORT_UNIT_UNIT(port->Unit.Hints)) + if (LV2_HAVE_PORT_UNIT_UNIT(portUnit->Hints)) { - switch (port->Unit.Unit) + switch (portUnit->Unit) { case LV2_PORT_UNIT_BAR: std::strncpy(strBuf, "bars", STR_MAX); @@ -1180,9 +1196,50 @@ public: const float fixedValue(pData->param.getFixedValue(parameterId, value)); fParamBuffers[parameterId] = fixedValue; - if (parameterId >= fRdfDescriptor->PortCount) + if (pData->param.data[parameterId].rindex >= static_cast(fRdfDescriptor->PortCount)) { - // TODO + const uint32_t rparamId = pData->param.data[parameterId].rindex - fRdfDescriptor->PortCount; + CARLA_SAFE_ASSERT_UINT2_RETURN(rparamId < fRdfDescriptor->ParameterCount, + rparamId, fRdfDescriptor->PortCount,); + + uint8_t atomBuf[256]; + lv2_atom_forge_set_buffer(&fAtomForge, atomBuf, sizeof(atomBuf)); + + LV2_Atom_Forge_Frame forgeFrame; + lv2_atom_forge_object(&fAtomForge, &forgeFrame, kUridNull, kUridPatchSet); + + lv2_atom_forge_key(&fAtomForge, kUridPatchPoperty); + lv2_atom_forge_urid(&fAtomForge, getCustomURID(fRdfDescriptor->Parameters[rparamId].URI)); + lv2_atom_forge_key(&fAtomForge, kUridPatchValue); + + switch (fRdfDescriptor->Parameters[rparamId].Type) + { + case LV2_PARAMETER_BOOL: + lv2_atom_forge_bool(&fAtomForge, fixedValue > 0.5f); + break; + case LV2_PARAMETER_INT: + lv2_atom_forge_int(&fAtomForge, fixedValue + 0.5f); + break; + case LV2_PARAMETER_LONG: + lv2_atom_forge_long(&fAtomForge, fixedValue + 0.5f); + break; + case LV2_PARAMETER_FLOAT: + lv2_atom_forge_float(&fAtomForge, fixedValue); + break; + case LV2_PARAMETER_DOUBLE: + lv2_atom_forge_double(&fAtomForge, fixedValue); + break; + default: + carla_stderr2("setParameterValue called for invalid parameter, expect issues!"); + break; + } + + lv2_atom_forge_pop(&fAtomForge, &forgeFrame); + + LV2_Atom* const atom((LV2_Atom*)atomBuf); + CARLA_SAFE_ASSERT(atom->size < sizeof(atomBuf)); + + fAtomBufferEvIn.put(atom, fEventsIn.ctrlIndex); } CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback); @@ -1316,7 +1373,39 @@ public: { if (fUI.type == UI::TYPE_NULL) { - CARLA_SAFE_ASSERT(!yesNo); + if (fFilePathURI.isNotEmpty()) + { + const char* const path = pData->engine->runFileCallback(FILE_CALLBACK_OPEN, false, "Open File", ""); + + if (path != nullptr && path[0] != '\0') + { + carla_stdout("LV2 file path to send: '%s'", path); + + uint8_t atomBuf[4096]; + lv2_atom_forge_set_buffer(&fAtomForge, atomBuf, sizeof(atomBuf)); + + LV2_Atom_Forge_Frame forgeFrame; + lv2_atom_forge_object(&fAtomForge, &forgeFrame, kUridNull, kUridPatchSet); + + lv2_atom_forge_key(&fAtomForge, kUridPatchPoperty); + lv2_atom_forge_urid(&fAtomForge, getCustomURID(fFilePathURI)); + + lv2_atom_forge_key(&fAtomForge, kUridPatchValue); + lv2_atom_forge_path(&fAtomForge, path, std::strlen(path)); + + lv2_atom_forge_pop(&fAtomForge, &forgeFrame); + + LV2_Atom* const atom((LV2_Atom*)atomBuf); + CARLA_SAFE_ASSERT(atom->size < sizeof(atomBuf)); + + fAtomBufferEvIn.put(atom, fEventsIn.ctrlIndex); + } + } + else + { + CARLA_SAFE_ASSERT(!yesNo); + } + pData->engine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, pData->id, 0, 0, 0.0f, nullptr); return; } @@ -1443,7 +1532,7 @@ public: if (fUI.handle == nullptr) { #ifndef LV2_UIS_ONLY_BRIDGES - if (fUI.type == UI::TYPE_EMBED && fUI.window == nullptr) + if (fUI.type == UI::TYPE_EMBED && fUI.rdfDescriptor->Type != LV2_UI_NONE && fUI.window == nullptr) { const char* msg = nullptr; @@ -1757,16 +1846,20 @@ public: for (uint32_t i=0; i < fRdfDescriptor->ParameterCount; ++i) { - const LV2_RDF_Parameter& rdfParam(fRdfDescriptor->Parameters[i]); - - if (rdfParam.Range == nullptr) - continue; - if (std::strcmp(rdfParam.Range, LV2_ATOM__Bool) != 0 && - std::strcmp(rdfParam.Range, LV2_ATOM__Int) != 0 && - std::strcmp(rdfParam.Range, LV2_ATOM__Float) != 0) - continue; - - params += 1; + switch (fRdfDescriptor->Parameters[i].Type) + { + case LV2_PARAMETER_BOOL: + case LV2_PARAMETER_INT: + // case LV2_PARAMETER_LONG: + case LV2_PARAMETER_FLOAT: + case LV2_PARAMETER_DOUBLE: + params += 1; + break; + case LV2_PARAMETER_PATH: + if (fFilePathURI.isEmpty()) + fFilePathURI = fRdfDescriptor->Parameters[i].URI; + break; + } } if ((pData->options & PLUGIN_OPTION_FORCE_STEREO) != 0 && aIns <= 1 && aOuts <= 1 && fExt.state == nullptr && fExt.worker == nullptr) @@ -2413,7 +2506,20 @@ public: for (uint32_t i=0; i < fRdfDescriptor->ParameterCount; ++i) { const LV2_RDF_Parameter& rdfParam(fRdfDescriptor->Parameters[i]); - const LV2_RDF_PortPoints portPoints(fRdfDescriptor->Parameters[i].Points); + + switch (rdfParam.Type) + { + case LV2_PARAMETER_BOOL: + case LV2_PARAMETER_INT: + // case LV2_PARAMETER_LONG: + case LV2_PARAMETER_FLOAT: + case LV2_PARAMETER_DOUBLE: + break; + default: + continue; + } + + const LV2_RDF_PortPoints& portPoints(rdfParam.Points); const uint32_t j = iCtrl++; pData->param.data[j].index = static_cast(j); @@ -2435,7 +2541,7 @@ public: if (min >= max) { - carla_stderr2("WARNING - Broken plugin parameter '%s': min >= max", fRdfDescriptor->Parameters[i].Label); + carla_stderr2("WARNING - Broken plugin parameter '%s': min >= max", rdfParam.Label); max = min + 0.1f; } @@ -2458,26 +2564,29 @@ public: else if (def > max) def = max; - if (std::strcmp(rdfParam.Range, LV2_ATOM__Bool) == 0) + switch (rdfParam.Type) { + case LV2_PARAMETER_BOOL: step = max - min; stepSmall = step; stepLarge = step; pData->param.data[j].hints |= PARAMETER_IS_BOOLEAN; - } - else if (std::strcmp(rdfParam.Range, LV2_ATOM__Int) == 0) - { + break; + + case LV2_PARAMETER_INT: + case LV2_PARAMETER_LONG: step = 1.0f; stepSmall = 1.0f; stepLarge = 10.0f; pData->param.data[j].hints |= PARAMETER_IS_INTEGER; - } - else - { - float range = max - min; + break; + + default: + const float range = max - min; step = range/100.0f; stepSmall = range/1000.0f; stepLarge = range/10.0f; + break; } if (rdfParam.Input) @@ -2544,8 +2653,9 @@ public: fAtomBufferWorkerInTmpData = new uint8_t[fAtomBufferWorkerIn.getSize()]; } - if (fUI.type != UI::TYPE_NULL && fEventsIn.count > 0 && (fEventsIn.data[0].type & CARLA_EVENT_DATA_ATOM) != 0) - fAtomBufferUiIn.createBuffer(eventBufferSize); + if (fRdfDescriptor->ParameterCount > 0 || + (fUI.type != UI::TYPE_NULL && fEventsIn.count > 0 && (fEventsIn.data[0].type & CARLA_EVENT_DATA_ATOM) != 0)) + fAtomBufferEvIn.createBuffer(eventBufferSize); if (fUI.type != UI::TYPE_NULL && fEventsOut.count > 0 && (fEventsOut.data[0].type & CARLA_EVENT_DATA_ATOM) != 0) { @@ -2570,7 +2680,7 @@ public: if (isRealtimeSafe()) pData->hints |= PLUGIN_IS_RTSAFE; - if (fUI.type != UI::TYPE_NULL) + if (fUI.type != UI::TYPE_NULL || fFilePathURI.isNotEmpty()) { pData->hints |= PLUGIN_HAS_CUSTOM_UI; @@ -3132,14 +3242,14 @@ public: // ---------------------------------------------------------------------------------------------------- // Message Input - if (fAtomBufferUiIn.tryLock()) + if (fAtomBufferEvIn.tryLock()) { - if (fAtomBufferUiIn.isDataAvailableForReading()) + if (fAtomBufferEvIn.isDataAvailableForReading()) { const LV2_Atom* atom; uint32_t j, portIndex; - for (; fAtomBufferUiIn.get(atom, portIndex);) + for (; fAtomBufferEvIn.get(atom, portIndex);) { j = (portIndex < fEventsIn.count) ? portIndex : fEventsIn.ctrlIndex; @@ -3151,7 +3261,7 @@ public: } } - fAtomBufferUiIn.unlock(); + fAtomBufferEvIn.unlock(); } if (fExt.worker != nullptr && fAtomBufferWorkerIn.tryLock()) @@ -3628,9 +3738,8 @@ public: evData.port->writeMidiEvent(currentFrame, static_cast(ev->body.size), data); } } - else //if (ev->body.type == kUridAtomBLANK) + else if (fAtomBufferUiOutTmpData != nullptr) { - //carla_stdout("Got out event, %s", carla_lv2_urid_unmap(this, ev->body.type)); fAtomBufferUiOut.put(&ev->body, evData.rindex); } @@ -4226,7 +4335,7 @@ public: void uiParameterChange(const uint32_t index, const float value) noexcept override { - CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL,); + CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL || fFilePathURI.isNotEmpty(),); CARLA_SAFE_ASSERT_RETURN(index < pData->param.count,); if (fUI.type == UI::TYPE_BRIDGE) @@ -4246,7 +4355,7 @@ public: void uiMidiProgramChange(const uint32_t index) noexcept override { - CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL,); + CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL || fFilePathURI.isNotEmpty(),); CARLA_SAFE_ASSERT_RETURN(index < pData->midiprog.count,); if (fUI.type == UI::TYPE_BRIDGE) @@ -4263,7 +4372,7 @@ public: void uiNoteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) noexcept override { - CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL,); + CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL || fFilePathURI.isNotEmpty(),); CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,); CARLA_SAFE_ASSERT_RETURN(note < MAX_MIDI_NOTE,); CARLA_SAFE_ASSERT_RETURN(velo > 0 && velo < MAX_MIDI_VALUE,); @@ -4293,7 +4402,7 @@ public: void uiNoteOff(const uint8_t channel, const uint8_t note) noexcept override { - CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL,); + CARLA_SAFE_ASSERT_RETURN(fUI.type != UI::TYPE_NULL || fFilePathURI.isNotEmpty(),); CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,); CARLA_SAFE_ASSERT_RETURN(note < MAX_MIDI_NOTE,); @@ -4817,7 +4926,7 @@ public: { CARLA_SAFE_ASSERT_RETURN(fExt.worker != nullptr && fExt.worker->work != nullptr, LV2_WORKER_ERR_UNKNOWN); CARLA_SAFE_ASSERT_RETURN(fEventsIn.ctrl != nullptr, LV2_WORKER_ERR_UNKNOWN); - carla_stdout("CarlaPluginLV2::handleWorkerSchedule(%i, %p)", size, data); + carla_debug("CarlaPluginLV2::handleWorkerSchedule(%i, %p)", size, data); if (pData->engine->isOffline()) { @@ -4835,7 +4944,7 @@ public: LV2_Worker_Status handleWorkerRespond(const uint32_t size, const void* const data) { CARLA_SAFE_ASSERT_RETURN(fExt.worker != nullptr && fExt.worker->work_response != nullptr, LV2_WORKER_ERR_UNKNOWN); - carla_stdout("CarlaPluginLV2::handleWorkerRespond(%i, %p)", size, data); + carla_debug("CarlaPluginLV2::handleWorkerRespond(%i, %p)", size, data); LV2_Atom atom; atom.size = size; @@ -5003,7 +5112,7 @@ public: index = fEventsIn.ctrlIndex; } - fAtomBufferUiIn.put(atom, index); + fAtomBufferEvIn.put(atom, index); } break; default: @@ -5759,6 +5868,10 @@ public: switch (uiType) { + case LV2_UI_NONE: + carla_stdout("Will use LV2 Show Interface for '%s'", pData->name); + fUI.type = UI::TYPE_EMBED; + break; case LV2_UI_QT4: carla_stdout("Will use LV2 Qt4 UI for '%s', NOT!", pData->name); fUI.type = UI::TYPE_EMBED; @@ -5922,7 +6035,7 @@ public: CARLA_SAFE_ASSERT_RETURN(atom != nullptr,); carla_debug("CarlaPluginLV2::handleTransferAtom(%i, %p)", portIndex, atom); - fAtomBufferUiIn.put(atom, portIndex); + fAtomBufferEvIn.put(atom, portIndex); } void handleUridMap(const LV2_URID urid, const char* const uri) @@ -5970,7 +6083,7 @@ private: int32_t fLatencyIndex; // -1 if invalid int fStrictBounds; // -1 unsupported, 0 optional, 1 required - Lv2AtomRingBuffer fAtomBufferUiIn; + Lv2AtomRingBuffer fAtomBufferEvIn; Lv2AtomRingBuffer fAtomBufferUiOut; Lv2AtomRingBuffer fAtomBufferWorkerIn; Lv2AtomRingBuffer fAtomBufferWorkerResp; @@ -5989,6 +6102,9 @@ private: void* fLastStateChunk; EngineTimeInfo fLastTimeInfo; + // if plugin provides path parameter, use it as fake "gui" + CarlaString fFilePathURI; + struct Extensions { const LV2_Options_Interface* options; const LV2_State_Interface* state; @@ -6307,6 +6423,14 @@ private: if (std::strcmp(uri, LV2_LOG__Warning) == 0) return kUridLogWarning; + // Patch types + if (std::strcmp(uri, LV2_PATCH__Set) == 0) + return kUridPatchSet; + if (std::strcmp(uri, LV2_PATCH__property) == 0) + return kUridPatchPoperty; + if (std::strcmp(uri, LV2_PATCH__value) == 0) + return kUridPatchValue; + // Time types if (std::strcmp(uri, LV2_TIME__Position) == 0) return kUridTimePosition; @@ -6427,6 +6551,14 @@ private: case kUridLogWarning: return LV2_LOG__Warning; + // Patch types + case kUridPatchSet: + return LV2_PATCH__Set; + case kUridPatchPoperty: + return LV2_PATCH__property; + case kUridPatchValue: + return LV2_PATCH__value; + // Time types case kUridTimePosition: return LV2_TIME__Position; diff --git a/source/includes/lv2_rdf.hpp b/source/includes/lv2_rdf.hpp index d9f947330..fa7582f2a 100644 --- a/source/includes/lv2_rdf.hpp +++ b/source/includes/lv2_rdf.hpp @@ -31,6 +31,15 @@ typedef const char* LV2_URI; typedef uint32_t LV2_Property; #define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) +// Parameter Types +#define LV2_PARAMETER_BOOL 1 +#define LV2_PARAMETER_INT 2 +#define LV2_PARAMETER_LONG 3 +#define LV2_PARAMETER_FLOAT 4 +#define LV2_PARAMETER_DOUBLE 5 +#define LV2_PARAMETER_PATH 6 +#define LV2_PARAMETER_STRING 7 + // Port Midi Map Types #define LV2_PORT_MIDI_MAP_CC 1 #define LV2_PORT_MIDI_MAP_NRPN 2 @@ -215,6 +224,7 @@ typedef uint32_t LV2_Property; #define LV2_IS_UI_PORT_PROTOCOL_PEAK(x) ((x) == LV2_UI_PORT_PROTOCOL_PEAK) // UI Types +#define LV2_UI_NONE 0 #define LV2_UI_GTK2 1 #define LV2_UI_GTK3 2 #define LV2_UI_QT4 3 @@ -446,7 +456,7 @@ struct LV2_RDF_Port { // Parameter struct LV2_RDF_Parameter { LV2_URI URI; - LV2_URI Range; + LV2_Property Type; bool Input; const char* Label; const char* Comment; @@ -457,7 +467,7 @@ struct LV2_RDF_Parameter { LV2_RDF_Parameter() noexcept : URI(nullptr), - Range(nullptr), + Type(0), Input(true), Label(nullptr), Comment(nullptr), @@ -472,11 +482,6 @@ struct LV2_RDF_Parameter { delete[] URI; URI = nullptr; } - if (Range != nullptr) - { - delete[] Range; - Range = nullptr; - } if (Label != nullptr) { delete[] Label; diff --git a/source/utils/CarlaLv2Utils.hpp b/source/utils/CarlaLv2Utils.hpp index 62f780e18..4a37ce9af 100644 --- a/source/utils/CarlaLv2Utils.hpp +++ b/source/utils/CarlaLv2Utils.hpp @@ -229,6 +229,7 @@ public: Lilv::Node unit_unit; // UI Types + Lilv::Node ui; Lilv::Node ui_gtk2; Lilv::Node ui_gtk3; Lilv::Node ui_qt4; @@ -238,7 +239,6 @@ public: Lilv::Node ui_x11; Lilv::Node ui_external; Lilv::Node ui_externalOld; - Lilv::Node ui_externalOld2; // Misc Lilv::Node atom_bufferType; @@ -365,6 +365,7 @@ public: unit_symbol (new_uri(LV2_UNITS__symbol)), unit_unit (new_uri(LV2_UNITS__unit)), + ui (new_uri(LV2_UI__UI)), ui_gtk2 (new_uri(LV2_UI__GtkUI)), ui_gtk3 (new_uri(LV2_UI__Gtk3UI)), ui_qt4 (new_uri(LV2_UI__Qt4UI)), @@ -374,7 +375,6 @@ public: ui_x11 (new_uri(LV2_UI__X11UI)), ui_external (new_uri(LV2_EXTERNAL_UI__Widget)), ui_externalOld (new_uri(LV2_EXTERNAL_UI_DEPRECATED_URI)), - ui_externalOld2 (new_uri("http://nedko.arnaudov.name/lv2/external_ui/")), atom_bufferType (new_uri(LV2_ATOM__bufferType)), atom_sequence (new_uri(LV2_ATOM__Sequence)), @@ -2153,7 +2153,7 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) else if (std::strcmp(unitUnit, LV2_UNITS__semitone12TET) == 0) rdfPort->Unit.Unit = LV2_PORT_UNIT_SEMITONE; else - carla_stderr("lv2_rdf_new(\"%s\") - got unknown unit unit '%s'", uri, unitUnit); + carla_stderr("lv2_rdf_new(\"%s\") - got unknown unit '%s'", uri, unitUnit); } } @@ -2214,7 +2214,6 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) if (const uint numScalePoints = lilvScalePoints.size()) { - rdfPort->ScalePointCount = numScalePoints; rdfPort->ScalePoints = new LV2_RDF_PortScalePoint[numScalePoints]; // get all scalepoints and sort them by value @@ -2234,11 +2233,11 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) } // now safe to store, sorted by using std::map - uint h = 0; + uint numUsed = 0; for (LilvScalePointMap::iterator it=sortedpoints.begin(), end=sortedpoints.end(); it != end; ++it) { - CARLA_SAFE_ASSERT_BREAK(h < numScalePoints); - LV2_RDF_PortScalePoint* const rdfScalePoint(&rdfPort->ScalePoints[h++]); + CARLA_SAFE_ASSERT_BREAK(numUsed < numScalePoints); + LV2_RDF_PortScalePoint* const rdfScalePoint(&rdfPort->ScalePoints[numUsed++]); const LilvScalePoint* const scalepoint = it->second; @@ -2248,6 +2247,8 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) rdfScalePoint->Label = carla_strdup(lilv_node_as_string(xlabel)); rdfScalePoint->Value = lilv_node_as_float(xvalue); } + + rdfPort->ScalePointCount = numUsed; } lilv_nodes_free(const_cast(lilvScalePoints.me)); @@ -2262,39 +2263,192 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) if (const uint numParameters = patchWritableNodes.size()) { - rdfDescriptor->ParameterCount = numParameters; rdfDescriptor->Parameters = new LV2_RDF_Parameter[numParameters]; - uint h = 0; + uint numUsed = 0; LILV_FOREACH(nodes, it, patchWritableNodes) { - CARLA_SAFE_ASSERT_BREAK(h < numParameters); + CARLA_SAFE_ASSERT_BREAK(numUsed < numParameters); Lilv::Node patchWritableNode(patchWritableNodes.get(it)); - LV2_RDF_Parameter* const rdfParam(&rdfDescriptor->Parameters[h++]); + LV2_RDF_Parameter& rdfParam(rdfDescriptor->Parameters[numUsed++]); CARLA_SAFE_ASSERT_CONTINUE(patchWritableNode.is_uri()); - rdfParam->URI = carla_strdup(patchWritableNode.as_uri()); + rdfParam.URI = carla_strdup(patchWritableNode.as_uri()); + + // ---------------------------------------------------------------------------------------------------- + // Set Basics - if (LilvNode* const label = lilv_world_get(lv2World.me, patchWritableNode, - lv2World.rdfs_range.me, nullptr)) + if (LilvNode* const rangeNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.rdfs_range.me, nullptr)) { - rdfParam->Range = carla_strdup(lilv_node_as_string(label)); + const char* const rangeURI = lilv_node_as_string(rangeNode); + + /**/ if (std::strcmp(rangeURI, LV2_ATOM__Bool) == 0) + rdfParam.Type = LV2_PARAMETER_BOOL; + else if (std::strcmp(rangeURI, LV2_ATOM__Int) == 0) + rdfParam.Type = LV2_PARAMETER_INT; + else if (std::strcmp(rangeURI, LV2_ATOM__Long) == 0) + rdfParam.Type = LV2_PARAMETER_LONG; + else if (std::strcmp(rangeURI, LV2_ATOM__Float) == 0) + rdfParam.Type = LV2_PARAMETER_FLOAT; + else if (std::strcmp(rangeURI, LV2_ATOM__Double) == 0) + rdfParam.Type = LV2_PARAMETER_DOUBLE; + else if (std::strcmp(rangeURI, LV2_ATOM__Path) == 0) + rdfParam.Type = LV2_PARAMETER_PATH; + else if (std::strcmp(rangeURI, LV2_ATOM__String) == 0) + rdfParam.Type = LV2_PARAMETER_STRING; + else + carla_stderr("lv2_rdf_new(\"%s\") - got unknown parameter type '%s'", uri, rangeURI); + + lilv_node_free(rangeNode); } - if (LilvNode* const label = lilv_world_get(lv2World.me, patchWritableNode, - lv2World.rdfs_label.me, nullptr)) + + if (LilvNode* const labelNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.rdfs_label.me, nullptr)) { - rdfParam->Label = carla_strdup(lilv_node_as_string(label)); + rdfParam.Label = carla_strdup(lilv_node_as_string(labelNode)); + lilv_node_free(labelNode); } - if (LilvNode* const comment = lilv_world_get(lv2World.me, patchWritableNode, - lv2World.rdfs_comment.me, nullptr)) + + if (LilvNode* const commentNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.rdfs_comment.me, nullptr)) { - rdfParam->Comment = carla_strdup(lilv_node_as_string(comment)); + rdfParam.Comment = carla_strdup(lilv_node_as_string(commentNode)); + lilv_node_free(commentNode); + } + + // ---------------------------------------------------------------------------------------------------- + // Set Port Points + + if (LilvNode* const defNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.value_default.me, nullptr)) + { + rdfParam.Points.Hints |= LV2_PORT_POINT_DEFAULT; + rdfParam.Points.Default = lilv_node_as_float(defNode); + lilv_node_free(defNode); + } + + if (LilvNode* const minNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.value_minimum.me, nullptr)) + { + rdfParam.Points.Hints |= LV2_PORT_POINT_MINIMUM; + rdfParam.Points.Minimum = lilv_node_as_float(minNode); + lilv_node_free(minNode); + } + + if (LilvNode* const maxNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.value_maximum.me, nullptr)) + { + rdfParam.Points.Hints |= LV2_PORT_POINT_MAXIMUM; + rdfParam.Points.Maximum = lilv_node_as_float(maxNode); + lilv_node_free(maxNode); } - // TODO: MidiMap, Points, Unit; + // ---------------------------------------------------------------------------------------------------- + // Set Port Unit + + if (LilvNode* const unitUnitNode = lilv_world_get(lv2World.me, patchWritableNode, + lv2World.unit_unit.me, nullptr)) + { + if (lilv_node_is_uri(unitUnitNode)) + { + if (const char* const unitUnit = lilv_node_as_uri(unitUnitNode)) + { + rdfParam.Unit.Hints |= LV2_PORT_UNIT_UNIT; + + /**/ if (std::strcmp(unitUnit, LV2_UNITS__bar) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_BAR; + else if (std::strcmp(unitUnit, LV2_UNITS__beat) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_BEAT; + else if (std::strcmp(unitUnit, LV2_UNITS__bpm) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_BPM; + else if (std::strcmp(unitUnit, LV2_UNITS__cent) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_CENT; + else if (std::strcmp(unitUnit, LV2_UNITS__cm) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_CM; + else if (std::strcmp(unitUnit, LV2_UNITS__coef) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_COEF; + else if (std::strcmp(unitUnit, LV2_UNITS__db) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_DB; + else if (std::strcmp(unitUnit, LV2_UNITS__degree) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_DEGREE; + else if (std::strcmp(unitUnit, LV2_UNITS__frame) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_FRAME; + else if (std::strcmp(unitUnit, LV2_UNITS__hz) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_HZ; + else if (std::strcmp(unitUnit, LV2_UNITS__inch) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_INCH; + else if (std::strcmp(unitUnit, LV2_UNITS__khz) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_KHZ; + else if (std::strcmp(unitUnit, LV2_UNITS__km) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_KM; + else if (std::strcmp(unitUnit, LV2_UNITS__m) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_M; + else if (std::strcmp(unitUnit, LV2_UNITS__mhz) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_MHZ; + else if (std::strcmp(unitUnit, LV2_UNITS__midiNote) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_MIDINOTE; + else if (std::strcmp(unitUnit, LV2_UNITS__mile) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_MILE; + else if (std::strcmp(unitUnit, LV2_UNITS__min) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_MIN; + else if (std::strcmp(unitUnit, LV2_UNITS__mm) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_MM; + else if (std::strcmp(unitUnit, LV2_UNITS__ms) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_MS; + else if (std::strcmp(unitUnit, LV2_UNITS__oct) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_OCT; + else if (std::strcmp(unitUnit, LV2_UNITS__pc) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_PC; + else if (std::strcmp(unitUnit, LV2_UNITS__s) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_S; + else if (std::strcmp(unitUnit, LV2_UNITS__semitone12TET) == 0) + rdfParam.Unit.Unit = LV2_PORT_UNIT_SEMITONE; + else + carla_stderr("lv2_rdf_new(\"%s\") - got unknown unit '%s'", uri, unitUnit); + } + } + + if (LilvNode* const unitNameNode = lilv_world_get(lv2World.me, unitUnitNode, + lv2World.unit_name.me, nullptr)) + { + if (const char* const unitName = lilv_node_as_string(unitNameNode)) + { + rdfParam.Unit.Hints |= LV2_PORT_UNIT_NAME; + rdfParam.Unit.Name = carla_strdup(unitName); + } + lilv_node_free(unitNameNode); + } + + if (LilvNode* const unitRenderNode = lilv_world_get(lv2World.me, unitUnitNode, + lv2World.unit_render.me, nullptr)) + { + if (const char* const unitRender = lilv_node_as_string(unitRenderNode)) + { + rdfParam.Unit.Hints |= LV2_PORT_UNIT_RENDER; + rdfParam.Unit.Render = carla_strdup(unitRender); + } + lilv_node_free(unitRenderNode); + } + + if (LilvNode* const unitSymbolNode = lilv_world_get(lv2World.me, unitUnitNode, + lv2World.unit_symbol.me, nullptr)) + { + if (const char* const unitSymbol = lilv_node_as_string(unitSymbolNode)) + { + rdfParam.Unit.Hints |= LV2_PORT_UNIT_SYMBOL; + rdfParam.Unit.Symbol = carla_strdup(unitSymbol); + } + lilv_node_free(unitSymbolNode); + } + + lilv_node_free(unitUnitNode); + } } + + rdfDescriptor->ParameterCount = numUsed; } lilv_nodes_free(const_cast(patchWritableNodes.me)); @@ -2405,16 +2559,15 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) { Lilv::Nodes lilvFeatureNodesR(lilvPlugin.get_required_features()); - rdfDescriptor->FeatureCount = numFeatures; rdfDescriptor->Features = new LV2_RDF_Feature[numFeatures]; - uint h = 0; + uint numUsed = 0; LILV_FOREACH(nodes, it, lilvFeatureNodes) { - CARLA_SAFE_ASSERT_BREAK(h < numFeatures); + CARLA_SAFE_ASSERT_BREAK(numUsed < numFeatures); Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it)); - LV2_RDF_Feature* const rdfFeature(&rdfDescriptor->Features[h++]); + LV2_RDF_Feature* const rdfFeature(&rdfDescriptor->Features[numUsed++]); rdfFeature->Required = lilvFeatureNodesR.contains(lilvFeatureNode); @@ -2424,6 +2577,7 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) rdfFeature->URI = nullptr; } + rdfDescriptor->FeatureCount = numUsed; lilv_nodes_free(const_cast(lilvFeatureNodesR.me)); } @@ -2437,16 +2591,15 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) if (const uint numExtensions = lilvExtensionDataNodes.size()) { - rdfDescriptor->ExtensionCount = numExtensions; rdfDescriptor->Extensions = new LV2_URI[numExtensions]; - uint h = 0; + uint numUsed = 0; LILV_FOREACH(nodes, it, lilvExtensionDataNodes) { - CARLA_SAFE_ASSERT_BREAK(h < numExtensions); + CARLA_SAFE_ASSERT_BREAK(numUsed < numExtensions); Lilv::Node lilvExtensionDataNode(lilvExtensionDataNodes.get(it)); - LV2_URI* const rdfExtension(&rdfDescriptor->Extensions[h++]); + LV2_URI* const rdfExtension(&rdfDescriptor->Extensions[numUsed++]); if (lilvExtensionDataNode.is_uri()) { @@ -2459,8 +2612,10 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) *rdfExtension = nullptr; } - for (uint32_t x=h; x < rdfDescriptor->ExtensionCount; ++x) + for (uint32_t x=numUsed; x < rdfDescriptor->ExtensionCount; ++x) rdfDescriptor->Extensions[x] = nullptr; + + rdfDescriptor->ExtensionCount = numUsed; } lilv_nodes_free(const_cast(lilvExtensionDataNodes.me)); @@ -2473,16 +2628,15 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) if (const uint numUIs = lilvUIs.size()) { - rdfDescriptor->UICount = numUIs; rdfDescriptor->UIs = new LV2_RDF_UI[numUIs]; - uint h = 0; + uint numUsed = 0; LILV_FOREACH(uis, it, lilvUIs) { - CARLA_SAFE_ASSERT_BREAK(h < numUIs); + CARLA_SAFE_ASSERT_BREAK(numUsed < numUIs); Lilv::UI lilvUI(lilvUIs.get(it)); - LV2_RDF_UI* const rdfUI(&rdfDescriptor->UIs[h++]); + LV2_RDF_UI* const rdfUI(&rdfDescriptor->UIs[numUsed++]); lv2World.load_resource(lilvUI.get_uri()); @@ -2507,8 +2661,8 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) rdfUI->Type = LV2_UI_EXTERNAL; else if (lilvUI.is_a(lv2World.ui_externalOld)) rdfUI->Type = LV2_UI_OLD_EXTERNAL; - else if (lilvUI.is_a(lv2World.ui_externalOld2)) - pass(); + else if (lilvUI.is_a(lv2World.ui)) + rdfUI->Type = LV2_UI_NONE; else carla_stderr("lv2_rdf_new(\"%s\") - UI '%s' is of unknown type", uri, lilvUI.get_uri().as_uri()); @@ -2534,16 +2688,15 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) { Lilv::Nodes lilvFeatureNodesR(lilvUI.get_required_features()); - rdfUI->FeatureCount = numFeatures; rdfUI->Features = new LV2_RDF_Feature[numFeatures]; - uint h2 = 0; + uint numUsed2 = 0; LILV_FOREACH(nodes, it2, lilvFeatureNodes) { - CARLA_SAFE_ASSERT_BREAK(h2 < numFeatures); + CARLA_SAFE_ASSERT_UINT2_BREAK(numUsed2 < numFeatures, numUsed2, numFeatures); Lilv::Node lilvFeatureNode(lilvFeatureNodes.get(it2)); - LV2_RDF_Feature* const rdfFeature(&rdfUI->Features[h2++]); + LV2_RDF_Feature* const rdfFeature(&rdfUI->Features[numUsed2++]); rdfFeature->Required = lilvFeatureNodesR.contains(lilvFeatureNode); @@ -2553,6 +2706,7 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) rdfFeature->URI = nullptr; } + rdfUI->FeatureCount = numUsed2; lilv_nodes_free(const_cast(lilvFeatureNodesR.me)); } @@ -2564,18 +2718,17 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) { Lilv::Nodes lilvExtensionDataNodes(lilvUI.get_extension_data()); - if (const uint numExtensions = lilvExtensionDataNodes.size() > 0) + if (const uint numExtensions = lilvExtensionDataNodes.size()) { - rdfUI->ExtensionCount = numExtensions; rdfUI->Extensions = new LV2_URI[numExtensions]; - uint h2 = 0; + uint numUsed2 = 0; LILV_FOREACH(nodes, it2, lilvExtensionDataNodes) { - CARLA_SAFE_ASSERT_BREAK(h2 < numExtensions); + CARLA_SAFE_ASSERT_UINT2_BREAK(numUsed2 < numExtensions, numUsed2, numExtensions); Lilv::Node lilvExtensionDataNode(lilvExtensionDataNodes.get(it2)); - LV2_URI* const rdfExtension(&rdfUI->Extensions[h2++]); + LV2_URI* const rdfExtension(&rdfUI->Extensions[numUsed2++]); if (lilvExtensionDataNode.is_uri()) { @@ -2588,8 +2741,10 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) *rdfExtension = nullptr; } - for (uint x2=h2; x2 < rdfUI->ExtensionCount; ++x2) + for (uint x2=numUsed2; x2 < rdfUI->ExtensionCount; ++x2) rdfUI->Extensions[x2] = nullptr; + + rdfUI->ExtensionCount = numUsed2; } lilv_nodes_free(const_cast(lilvExtensionDataNodes.me)); @@ -2605,13 +2760,13 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) rdfUI->PortNotificationCount = portNotifCount; rdfUI->PortNotifications = new LV2_RDF_UI_PortNotification[portNotifCount]; - uint h2 = 0; + uint numUsed2 = 0; LILV_FOREACH(nodes, it2, portNotifNodes) { - CARLA_SAFE_ASSERT_BREAK(h2 < portNotifCount); + CARLA_SAFE_ASSERT_UINT2_BREAK(numUsed2 < portNotifCount, numUsed2, portNotifCount); Lilv::Node portNotifNode(portNotifNodes.get(it2)); - LV2_RDF_UI_PortNotification* const rdfPortNotif(&rdfUI->PortNotifications[h2++]); + LV2_RDF_UI_PortNotification* const rdfPortNotif(&rdfUI->PortNotifications[numUsed2++]); LilvNode* const protocolNode = lilv_world_get(lv2World.me, portNotifNode, lv2World.ui_protocol.me, nullptr); @@ -2665,6 +2820,8 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) lilv_nodes_free(const_cast(portNotifNodes.me)); } } + + rdfDescriptor->UICount = numUsed; } lilv_nodes_free(const_cast(lilvUIs.me));