Implement Lv2 Urid feature (#5517)

This includes implementing general feature handling, since this is the first supported feature.
This commit is contained in:
Johannes Lorenz
2020-08-09 22:59:37 +02:00
committed by GitHub
parent df296b7931
commit 7a9b33627d
9 changed files with 417 additions and 3 deletions

View File

@@ -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

View 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

View File

@@ -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)
{

View File

@@ -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;

View 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