From a2e71c81dec5e17dcabc05ea39360f1c9766ce26 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 15 Nov 2020 22:44:52 +0100 Subject: [PATCH] Lv2: Use port-property "logarithmic" This also adds more min/max checks, mostly for logarithmic scales. Since this raised some warnings for logarithmic CV ports, the CV metadata is now also read (but CV ports are still not supported). --- include/Lv2Ports.h | 2 + include/PluginIssue.h | 9 +++ src/core/PluginIssue.cpp | 8 ++ src/core/lv2/Lv2Ports.cpp | 153 +++++++++++++++++++++++++------------- src/core/lv2/Lv2Proc.cpp | 7 +- 5 files changed, 128 insertions(+), 51 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index a0d68f24fd..1b37d518b3 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -108,6 +108,8 @@ struct Meta Flow m_flow = Flow::Unknown; Vis m_vis = Vis::None; + bool m_logarithmic = false; + bool m_optional = false; bool m_used = true; diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 18bb6c5e74..8bfad5bf10 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -32,18 +32,27 @@ //! LMMS Plugins should use this to indicate errors enum PluginIssueType { + // port flow & type unknownPortFlow, unknownPortType, + // channel count tooManyInputChannels, tooManyOutputChannels, tooManyMidiInputChannels, tooManyMidiOutputChannels, noOutputChannel, + // port metadata portHasNoDef, portHasNoMin, portHasNoMax, + minGreaterMax, defaultValueNotInRange, + logScaleMinMissing, + logScaleMaxMissing, + logScaleMinMaxDifferentSigns, + // features featureNotSupported, //!< plugin requires functionality LMMS can't offer + // misc badPortType, //!< port type not supported blacklisted, noIssue diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 1b54fb41cc..7d43586f5b 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -50,8 +50,16 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "port is missing min value"; case portHasNoMax: return "port is missing max value"; + case minGreaterMax: + return "port minimum is greater than maximum"; case defaultValueNotInRange: return "default value is not in range [min, max]"; + case logScaleMinMissing: + return "logscale requires minimum value"; + case logScaleMaxMissing: + return "logscale requires maximum value"; + case logScaleMinMaxDifferentSigns: + return "logscale with min < 0 < max"; case featureNotSupported: return "required feature not supported"; case badPortType: diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 3bca0c89f1..4bd77f0bcb 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -28,6 +28,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include "Engine.h" #include "Lv2Basics.h" @@ -126,75 +127,106 @@ std::vector Meta::get(const LilvPlugin *plugin, issue(unknownPortFlow, portName); } - m_def = .0f; m_min = .0f; m_max = .0f; + m_def = .0f; + m_min = std::numeric_limits::lowest(); + m_max = std::numeric_limits::max(); + auto m_min_set = [this]{ return m_min != std::numeric_limits::lowest(); }; + auto m_max_set = [this]{ return m_max != std::numeric_limits::max(); }; m_type = Type::Unknown; - if (isA(LV2_CORE__ControlPort)) + if (isA(LV2_CORE__ControlPort) || isA(LV2_CORE__CVPort)) { - m_type = Type::Control; + // Read metadata for control ports + // CV ports are mostly the same as control ports, so we take + // mostly the same metadata - if (m_flow == Flow::Input) + if (isA(LV2_CORE__CVPort)) { - bool isToggle = m_vis == Vis::Toggled; + // currently not supported, but we can still check the metadata + issue(badPortType, "cvPort"); + } - LilvNode * defN, * minN = nullptr, * maxN = nullptr; - lilv_port_get_range(plugin, lilvPort, &defN, - isToggle ? nullptr : &minN, - isToggle ? nullptr : &maxN); - AutoLilvNode def(defN), min(minN), max(maxN); + m_type = isA(LV2_CORE__CVPort) ? Type::Cv : Type::Control; - auto takeRangeValue = [&](LilvNode* node, - float& storeHere, PluginIssueType it) - { - if (node) { storeHere = lilv_node_as_float(node); } - else { issue(it, portName); } - }; + bool isToggle = m_vis == Vis::Toggled; - takeRangeValue(def.get(), m_def, portHasNoDef); - if (isToggle) - { - m_min = .0f; - m_max = 1.f; - if(def.get() && m_def != m_min && m_def != m_max) - { - issue(defaultValueNotInRange, portName); - } - } + LilvNode * defN, * minN = nullptr, * maxN = nullptr; + lilv_port_get_range(plugin, lilvPort, &defN, + isToggle ? nullptr : &minN, + isToggle ? nullptr : &maxN); + AutoLilvNode def(defN), min(minN), max(maxN); + + auto takeRangeValue = [&](LilvNode* node, + float& storeHere, PluginIssueType it) + { + if (node) { storeHere = lilv_node_as_float(node); } else { - takeRangeValue(min.get(), m_min, portHasNoMin); - takeRangeValue(max.get(), m_max, portHasNoMax); - if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } - - if (def.get()) + // CV ports do not require ranges + if(m_flow == Flow::Input && m_type != Type::Cv) { - if (m_def < m_min) { issue(defaultValueNotInRange, portName); } - else if (m_def > m_max) + issue(it, portName); + } + } + }; + + takeRangeValue(def.get(), m_def, portHasNoDef); + if (isToggle) + { + m_min = .0f; + m_max = 1.f; + if(def.get() && m_def != m_min && m_def != m_max) + { + issue(defaultValueNotInRange, portName); + } + } + else + { + // take min/max + takeRangeValue(min.get(), m_min, portHasNoMin); + takeRangeValue(max.get(), m_max, portHasNoMax); + if(m_type == Type::Cv) + { + // no range is allowed and bashed to [-1,+1], + // but only min or only max does not make sense + if(!m_min_set() && !m_max_set()) + { + m_min = -1.f; + m_max = +1.f; + } + else if(!m_min_set()) { issue(portHasNoMin, portName); } + else if(!m_max_set()) { issue(portHasNoMax, portName); } + } + if(m_min > m_max) { issue(minGreaterMax, portName); } + + // sampleRate + if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } + + // default value + if (def.get()) + { + if (m_def < m_min) { issue(defaultValueNotInRange, portName); } + else if (m_def > m_max) + { + if(m_sampleRate) { - if(m_sampleRate) - { - // multiplying with sample rate will hopefully lead us - // to a good default value - } - else { issue(defaultValueNotInRange, portName); } + // multiplying with sample rate will hopefully lead us + // to a good default value } + else { issue(defaultValueNotInRange, portName); } } + } - if (m_max - m_min > 15.0f) - { - // range too large for spinbox visualisation, use knobs - // e.g. 0...15 would be OK - m_vis = Vis::None; - } + // visualization + if (m_max - m_min > 15.0f) + { + // range too large for spinbox visualisation, use knobs + // e.g. 0...15 would be OK + m_vis = Vis::None; } } } else if (isA(LV2_CORE__AudioPort)) { m_type = Type::Audio; } - else if (isA(LV2_CORE__CVPort)) - { - issue(badPortType, "cvPort"); - m_type = Type::Cv; - } else if (isA(LV2_ATOM__AtomPort)) { AutoLilvNode uriAtomSequence(Engine::getLv2Manager()->uri(LV2_ATOM__Sequence)); @@ -221,6 +253,27 @@ std::vector Meta::get(const LilvPlugin *plugin, } } + if (hasProperty(LV2_PORT_PROPS__logarithmic)) + { + // check min/max available + // we requre them anyways, but this will detect plugins that will + // be non-Lv2-conforming + if(m_min == std::numeric_limits::lowest()) + { + issue(PluginIssueType::logScaleMinMissing, portName); + } + if(m_max == std::numeric_limits::max()) + { + issue(PluginIssueType::logScaleMaxMissing, portName); + } + // forbid min < 0 < max + if(m_min < 0.f && m_max > 0.f) + { + issue(PluginIssueType::logScaleMinMaxDifferentSigns, portName); + } + m_logarithmic = true; + } + return portIssues; } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index e02991789d..0ab204ae97 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -520,7 +520,12 @@ void Lv2Proc::createPort(std::size_t portNum) nullptr, dispName)); break; } - } + if(meta.m_logarithmic) + { + ctrl->m_connectedModel->setScaleLogarithmic(); + } + + } // if m_flow == Input port = ctrl; break; }