Implement Lv2 Urid feature (#5517)
This includes implementing general feature handling, since this is the first supported feature.
This commit is contained in:
81
include/Lv2Features.h
Normal file
81
include/Lv2Features.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Lv2Features.h - Lv2Features class
|
||||
*
|
||||
* Copyright (c) 2020-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* 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 <lv2.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#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 LV2_Feature* const* featurePointers() const
|
||||
{
|
||||
return m_featurePointers.data();
|
||||
}
|
||||
|
||||
private:
|
||||
//! feature storage
|
||||
std::vector<LV2_Feature> m_features;
|
||||
//! pointers to m_features, required for lilv_plugin_instantiate
|
||||
std::vector<const LV2_Feature*> m_featurePointers;
|
||||
//! features + data, ordered by URI
|
||||
std::map<const char*, void*, Lv2Manager::CmpStr> m_featureByUri;
|
||||
};
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
|
||||
#endif // LV2FEATURES_H
|
||||
@@ -30,9 +30,11 @@
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <lilv/lilv.h>
|
||||
|
||||
#include "Lv2Basics.h"
|
||||
#include "Lv2UridMap.h"
|
||||
#include "Plugin.h"
|
||||
|
||||
|
||||
@@ -114,10 +116,31 @@ public:
|
||||
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<const char*, CmpStr>& 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<const char*, CmpStr> m_supportedFeatureURIs;
|
||||
|
||||
// feature data that are common for all Lv2Proc
|
||||
UridMap m_uridMap;
|
||||
|
||||
// functions
|
||||
bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr);
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <QObject>
|
||||
|
||||
#include "Lv2Basics.h"
|
||||
#include "Lv2Features.h"
|
||||
#include "LinkedModelGroups.h"
|
||||
#include "Plugin.h"
|
||||
#include "PluginIssue.h"
|
||||
@@ -156,6 +157,7 @@ private:
|
||||
|
||||
const LilvPlugin* m_plugin;
|
||||
LilvInstance* m_instance;
|
||||
Lv2Features m_features;
|
||||
|
||||
std::vector<std::unique_ptr<Lv2Ports::PortBase>> m_ports;
|
||||
StereoPortRef m_inPorts, m_outPorts;
|
||||
@@ -163,6 +165,8 @@ private:
|
||||
//! models for the controls, sorted by port symbols
|
||||
std::map<std::string, AutomatableModel *> 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
|
||||
|
||||
69
include/Lv2UridMap.h
Normal file
69
include/Lv2UridMap.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Lv2UridMap.cpp - Lv2UridMap class
|
||||
*
|
||||
* Copyright (c) 2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* 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 LV2URIDMAP_H
|
||||
#define LV2URIDMAP_H
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
|
||||
#include <mutex> // TODO: use semaphore, even though this is not realtime critical
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Complete implementation of the Lv2 Urid Map extension
|
||||
*/
|
||||
class UridMap
|
||||
{
|
||||
std::unordered_map<std::string, LV2_URID> m_map;
|
||||
std::vector<const char*> m_unMap;
|
||||
|
||||
//! mutex for both m_map and m_unMap
|
||||
//! the URID map is global, which is why a mutex is required here
|
||||
std::mutex m_MapMutex;
|
||||
|
||||
LV2_URID_Map m_mapFeature;
|
||||
LV2_URID_Unmap m_unmapFeature;
|
||||
|
||||
LV2_URID m_lastUrid = 0;
|
||||
|
||||
public:
|
||||
//! constructor; will set up the features
|
||||
UridMap();
|
||||
|
||||
//! map feature function
|
||||
LV2_URID map(const char* uri);
|
||||
//! unmap feature function
|
||||
const char* unmap(LV2_URID urid);
|
||||
|
||||
// access the features
|
||||
LV2_URID_Map* mapFeature() { return &m_mapFeature; }
|
||||
LV2_URID_Unmap* unmapFeature() { return &m_unmapFeature; }
|
||||
};
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
#endif // LV2URIDMAP_H
|
||||
@@ -93,10 +93,12 @@ 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
|
||||
core/lv2/Lv2SubPluginFeatures.cpp
|
||||
core/lv2/Lv2UridMap.cpp
|
||||
|
||||
core/midi/MidiAlsaRaw.cpp
|
||||
core/midi/MidiAlsaSeq.cpp
|
||||
|
||||
97
src/core/lv2/Lv2Features.cpp
Normal file
97
src/core/lv2/Lv2Features.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Lv2Features.cpp - Lv2Features implementation
|
||||
*
|
||||
* Copyright (c) 2020-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* 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 <QtGlobal>
|
||||
|
||||
#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<const char* const, void*>& pr : m_featureByUri)
|
||||
{
|
||||
Q_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);
|
||||
Q_ASSERT(itr != m_featureByUri.end());
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <lilv/lilv.h>
|
||||
#include <lv2.h>
|
||||
#include <QDebug>
|
||||
@@ -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,22 @@ 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
|
||||
{
|
||||
return m_supportedFeatureURIs.find(featName) != m_supportedFeatureURIs.end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// unused + untested yet
|
||||
bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr)
|
||||
{
|
||||
|
||||
@@ -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,8 +75,12 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin));
|
||||
LILV_FOREACH (nodes, itr, reqFeats.get())
|
||||
{
|
||||
issues.emplace_back(featureNotSupported,
|
||||
lilv_node_as_string(lilv_nodes_get(reqFeats.get(), itr)));
|
||||
const char* reqFeatName = lilv_node_as_string(
|
||||
lilv_nodes_get(reqFeats.get(), itr));
|
||||
if(!Lv2Features::isFeatureSupported(reqFeatName))
|
||||
{
|
||||
issues.emplace_back(featureNotSupported, reqFeatName);
|
||||
}
|
||||
}
|
||||
|
||||
if (printIssues && issues.size())
|
||||
@@ -240,11 +245,15 @@ AutomatableModel *Lv2Proc::modelAtPort(const QString &uri)
|
||||
|
||||
void Lv2Proc::initPlugin()
|
||||
{
|
||||
m_features.initCommon();
|
||||
initPluginSpecificFeatures();
|
||||
m_features.createFeatureVectors();
|
||||
|
||||
createPorts();
|
||||
|
||||
m_instance = lilv_plugin_instantiate(m_plugin,
|
||||
Engine::mixer()->processingSampleRate(),
|
||||
nullptr);
|
||||
m_features.featurePointers());
|
||||
|
||||
if (m_instance)
|
||||
{
|
||||
@@ -276,6 +285,16 @@ void Lv2Proc::shutdownPlugin()
|
||||
|
||||
|
||||
|
||||
void Lv2Proc::initPluginSpecificFeatures()
|
||||
{
|
||||
// nothing yet
|
||||
// it would look like this:
|
||||
// m_features[LV2_URID__map] = m_uridMapFeature
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Lv2Proc::loadFileInternal(const QString &file)
|
||||
{
|
||||
(void)file;
|
||||
|
||||
99
src/core/lv2/Lv2UridMap.cpp
Normal file
99
src/core/lv2/Lv2UridMap.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Lv2UridMap.cpp - Lv2UridMap implementation
|
||||
*
|
||||
* Copyright (c) 2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* 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 "Lv2UridMap.h"
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
static LV2_URID staticMap(LV2_URID_Map_Handle handle, const char* uri)
|
||||
{
|
||||
UridMap* map = static_cast<UridMap*>(handle);
|
||||
return map->map(uri);
|
||||
}
|
||||
|
||||
static const char* staticUnmap(LV2_URID_Unmap_Handle handle, LV2_URID urid)
|
||||
{
|
||||
UridMap* map = static_cast<UridMap*>(handle);
|
||||
return map->unmap(urid);
|
||||
}
|
||||
|
||||
UridMap::UridMap()
|
||||
{
|
||||
m_mapFeature.handle = static_cast<LV2_URID_Map_Handle>(this);
|
||||
m_mapFeature.map = staticMap;
|
||||
m_unmapFeature.handle = static_cast<LV2_URID_Unmap_Handle>(this);
|
||||
m_unmapFeature.unmap = staticUnmap;
|
||||
}
|
||||
|
||||
LV2_URID UridMap::map(const char *uri)
|
||||
{
|
||||
LV2_URID result = 0u;
|
||||
|
||||
// the Lv2 docs say that 0 should be returned in any case
|
||||
// where creating an ID for the given URI fails
|
||||
try
|
||||
{
|
||||
// TODO:
|
||||
// when using C++14, we can get around any string allocation
|
||||
// in the case the URI is already inside the map:
|
||||
// * use `m_map.find(uri)` instead of `m_map.find(uriStr)`
|
||||
// * to avoid temporary string construction in the `find` call, create
|
||||
// m_map like this:
|
||||
// std::unordered_map<std::string, LV2_URID,
|
||||
// std::hash<std::string>, std::equal<>> m_map;
|
||||
// * move the try block inside the case where the URI is not in the map
|
||||
const std::string uriStr = uri;
|
||||
|
||||
std::lock_guard<std::mutex> guard (m_MapMutex);
|
||||
|
||||
auto itr = m_map.find(uriStr);
|
||||
if (itr == m_map.end())
|
||||
{
|
||||
// 1 is the first free URID
|
||||
std::size_t index = 1u + m_unMap.size();
|
||||
auto pr = m_map.emplace(std::move(uriStr), index);
|
||||
if (pr.second)
|
||||
{
|
||||
m_unMap.emplace_back(pr.first->first.c_str());
|
||||
result = static_cast<LV2_URID>(index);
|
||||
}
|
||||
}
|
||||
else { result = itr->second; }
|
||||
}
|
||||
catch(...) { /* result variable is already 0 */ }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *UridMap::unmap(LV2_URID urid)
|
||||
{
|
||||
std::size_t idx = static_cast<std::size_t>(urid) - 1;
|
||||
|
||||
std::lock_guard<std::mutex> guard (m_MapMutex);
|
||||
return (idx < m_unMap.size()) ? m_unMap[idx] : nullptr;
|
||||
}
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
|
||||
Reference in New Issue
Block a user