From 1cfa7132b945d46e80fd6c68fbe7ee12eadcc779 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 25 Jul 2020 05:21:12 +0200 Subject: [PATCH] Refactor supported Lv2 features ... from multiple places in Lv2Proc into Lv2Manager. --- include/Lv2Features.h | 81 ++++++++++++++++++++++++++++++ include/Lv2Manager.h | 19 ++++++++ include/Lv2Proc.h | 5 +- src/core/CMakeLists.txt | 1 + src/core/lv2/Lv2Features.cpp | 95 ++++++++++++++++++++++++++++++++++++ src/core/lv2/Lv2Manager.cpp | 26 ++++++++++ src/core/lv2/Lv2Proc.cpp | 30 ++++++++---- 7 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 include/Lv2Features.h create mode 100644 src/core/lv2/Lv2Features.cpp diff --git a/include/Lv2Features.h b/include/Lv2Features.h new file mode 100644 index 00000000000..5318034966d --- /dev/null +++ b/include/Lv2Features.h @@ -0,0 +1,81 @@ +/* + * Lv2Features.h - Lv2Features class + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LV2FEATURES_H +#define LV2FEATURES_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include "Lv2Manager.h" + +/** + Feature container + + References all available features for a plugin and maps them to their URIs. + + The public member functions should be called in descending order: + + 1. initCommon: map plugin-common features + 2. operator[]: map plugin-specific features + 3. createFeatureVectors: create the feature vectors required for + lilv_plugin_instantiate + 4. access the latter +*/ +class Lv2Features +{ +public: + //! Return if a feature is supported by LMMS + static bool isFeatureSupported(const char *featName); + + Lv2Features(); + + //! Register only plugin-common features + void initCommon(); + //! Return reference to feature data with given URI featName + void*& operator[](const char* featName); + //! Fill m_features and m_featurePointers with all features + void createFeatureVectors(); + //! Return LV2_Feature pointer vector, suited for lilv_plugin_instantiate + const std::vector& featurePointers() const + { + return m_featurePointers; + } + +private: + //! feature storage + std::vector m_features; + //! pointers to m_features, required for lilv_plugin_instantiate + std::vector m_featurePointers; + //! features + data, ordered by URI + std::map m_featureByUri; +}; + +#endif // LMMS_HAVE_LV2 + +#endif // LV2FEATURES_H diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 4e96381b019..0b5fc692354 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -30,6 +30,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include "Lv2Basics.h" @@ -115,13 +116,31 @@ class Lv2Manager Iterator begin() { return m_lv2InfoMap.begin(); } Iterator end() { return m_lv2InfoMap.end(); } + //! strcmp based key comparator for std::set and std::map + struct CmpStr + { + bool operator()(char const *a, char const *b) const; + }; + UridMap& uridMap() { return m_uridMap; } + //! Return all + const std::set& supportedFeatureURIs() const + { + return m_supportedFeatureURIs; + } + bool isFeatureSupported(const char* featName) const; private: + // general data bool m_debug; //!< if set, debug output will be printed LilvWorld* m_world; Lv2InfoMap m_lv2InfoMap; + std::set m_supportedFeatureURIs; + + // feature data that are common for all Lv2Proc UridMap m_uridMap; + + // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index bee06145d1e..1c1cc11d8ac 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -34,6 +34,7 @@ #include #include "Lv2Basics.h" +#include "Lv2Features.h" #include "LinkedModelGroups.h" #include "Plugin.h" #include "PluginIssue.h" @@ -156,7 +157,7 @@ class Lv2Proc : public LinkedModelGroup const LilvPlugin* m_plugin; LilvInstance* m_instance; - std::vector m_features; + Lv2Features m_features; std::vector> m_ports; StereoPortRef m_inPorts, m_outPorts; @@ -164,6 +165,8 @@ class Lv2Proc : public LinkedModelGroup //! models for the controls, sorted by port symbols std::map m_connectedModels; + void initPluginSpecificFeatures(); + //! load a file in the plugin, but don't do anything in LMMS void loadFileInternal(const QString &file); //! allocate m_ports, fill all with metadata, and assign meaning of ports diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index dcc8d37858a..10d50b70275 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -92,6 +92,7 @@ set(LMMS_SRCS core/lv2/Lv2Basics.cpp core/lv2/Lv2ControlBase.cpp + core/lv2/Lv2Features.cpp core/lv2/Lv2Ports.cpp core/lv2/Lv2Proc.cpp core/lv2/Lv2Manager.cpp diff --git a/src/core/lv2/Lv2Features.cpp b/src/core/lv2/Lv2Features.cpp new file mode 100644 index 00000000000..1821373d71d --- /dev/null +++ b/src/core/lv2/Lv2Features.cpp @@ -0,0 +1,95 @@ +/* + * Lv2Features.cpp - Lv2Features implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "Lv2Features.h" + +#ifdef LMMS_HAVE_LV2 + +#include "Engine.h" +#include "Lv2Manager.h" + + +bool Lv2Features::isFeatureSupported(const char* featName) +{ + return Engine::getLv2Manager()->isFeatureSupported(featName); +} + + + + +Lv2Features::Lv2Features() +{ + const Lv2Manager* man = Engine::getLv2Manager(); + // create (yet empty) map feature URI -> feature + for(const char* uri : man->supportedFeatureURIs()) + { + m_featureByUri.emplace(uri, nullptr); + } +} + + + + +void Lv2Features::initCommon() +{ + Lv2Manager* man = Engine::getLv2Manager(); + // init m_featureByUri with the plugin-common features + operator[](LV2_URID__map) = man->uridMap().mapFeature(); + operator[](LV2_URID__unmap) = man->uridMap().unmapFeature(); +} + + + + +void Lv2Features::createFeatureVectors() +{ + // create vector of features + for(std::pair& pr : m_featureByUri) + { + assert(pr.second != nullptr); + m_features.push_back(LV2_Feature { pr.first, pr.second }); + } + + // create pointer vector (for lilv_plugin_instantiate) + m_featurePointers.reserve(m_features.size() + 1); + for(std::size_t i = 0; i < m_features.size(); ++i) + { + m_featurePointers.push_back(&m_features[i]); + } + m_featurePointers.push_back(nullptr); +} + + + + +void *&Lv2Features::operator[](const char *featName) +{ + auto itr = m_featureByUri.find(featName); + assert (itr != m_featureByUri.end()); + return itr->second; +} + + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index bce3bf372ca..e3a06fb7f5b 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -27,6 +27,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include #include @@ -50,6 +51,9 @@ Lv2Manager::Lv2Manager() m_world = lilv_world_new(); lilv_world_load_all(m_world); + + m_supportedFeatureURIs.insert(LV2_URID__map); + m_supportedFeatureURIs.insert(LV2_URID__unmap); } @@ -133,6 +137,28 @@ void Lv2Manager::initPlugins() +bool Lv2Manager::CmpStr::operator()(const char *a, const char *b) const +{ + return std::strcmp(a, b) < 0; +} + + + + +bool Lv2Manager::isFeatureSupported(const char *featName) const +{ + bool found = false; + for(const char* supportedFeatName : supportedFeatureURIs()) + if(!strcmp(featName, supportedFeatName)) + { + found = true; break; + } + return found; +} + + + + // unused + untested yet bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 40596312c77..4c8f66314c7 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -31,6 +31,7 @@ #include "AutomatableModel.h" #include "ComboBoxModel.h" #include "Engine.h" +#include "Lv2Features.h" #include "Lv2Manager.h" #include "Lv2Ports.h" #include "Mixer.h" @@ -74,11 +75,12 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); LILV_FOREACH (nodes, itr, reqFeats.get()) { - const char* featName = lilv_node_as_string( + const char* reqFeatName = lilv_node_as_string( lilv_nodes_get(reqFeats.get(), itr)); - if( strcmp(featName, LV2_URID__map) - && strcmp(featName, LV2_URID__unmap)) - issues.emplace_back(featureNotSupported, featName); + if(!Lv2Features::isFeatureSupported(reqFeatName)) + { + issues.emplace_back(featureNotSupported, reqFeatName); + } } if (printIssues && issues.size()) @@ -243,15 +245,15 @@ AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) void Lv2Proc::initPlugin() { - Lv2Manager* man = Engine::getLv2Manager(); - m_features.push_back(new LV2_Feature { LV2_URID__map, man->uridMap().mapFeature() }); - m_features.push_back(new LV2_Feature { LV2_URID__unmap, man->uridMap().unmapFeature() }); - m_features.push_back(nullptr); + m_features.initCommon(); + initPluginSpecificFeatures(); + m_features.createFeatureVectors(); + createPorts(); m_instance = lilv_plugin_instantiate(m_plugin, Engine::mixer()->processingSampleRate(), - m_features.data()); + m_features.featurePointers().data()); if (m_instance) { @@ -278,8 +280,16 @@ void Lv2Proc::shutdownPlugin() lilv_instance_deactivate(m_instance); lilv_instance_free(m_instance); m_instance = nullptr; +} - for(LV2_Feature* feat : m_features) { delete feat; } + + + +void Lv2Proc::initPluginSpecificFeatures() +{ + // nothing yet + // it would look like this: + // m_features[LV2_URID__map] = m_uridMapFeature }