Implement Lv2 Options (#5761)

Implement `LV2_OPTIONS__options` feature and some buf-size properties.

The code currently assumes that the LMMS buffersize never changes, which
is currently true in the LMMS code base.
This commit is contained in:
Johannes Lorenz
2020-12-08 00:12:04 +01:00
committed by GitHub
parent cd2366a21c
commit 2f66449092
9 changed files with 307 additions and 5 deletions

View File

@@ -100,6 +100,7 @@ set(LMMS_SRCS
core/lv2/Lv2Ports.cpp
core/lv2/Lv2Proc.cpp
core/lv2/Lv2Manager.cpp
core/lv2/Lv2Options.cpp
core/lv2/Lv2SubPluginFeatures.cpp
core/lv2/Lv2UridCache.cpp
core/lv2/Lv2UridMap.cpp

View File

@@ -31,6 +31,7 @@
#include <cstring>
#include <lilv/lilv.h>
#include <lv2.h>
#include <lv2/lv2plug.in/ns/ext/options/options.h>
#include <QDebug>
#include <QDir>
#include <QLibrary>
@@ -41,6 +42,7 @@
#include "Plugin.h"
#include "PluginFactory.h"
#include "Lv2ControlBase.h"
#include "Lv2Options.h"
#include "PluginIssue.h"
@@ -67,6 +69,17 @@ Lv2Manager::Lv2Manager() :
m_supportedFeatureURIs.insert(LV2_URID__map);
m_supportedFeatureURIs.insert(LV2_URID__unmap);
m_supportedFeatureURIs.insert(LV2_OPTIONS__options);
auto supportOpt = [this](Lv2UridCache::Id id)
{
Lv2Options::supportOption(uridCache()[id]);
};
supportOpt(Lv2UridCache::Id::param_sampleRate);
supportOpt(Lv2UridCache::Id::bufsz_maxBlockLength);
supportOpt(Lv2UridCache::Id::bufsz_minBlockLength);
supportOpt(Lv2UridCache::Id::bufsz_nominalBlockLength);
supportOpt(Lv2UridCache::Id::bufsz_sequenceSize);
}
@@ -205,6 +218,15 @@ bool Lv2Manager::isFeatureSupported(const char *featName) const
AutoLilvNodes Lv2Manager::findNodes(const LilvNode *subject,
const LilvNode *predicate, const LilvNode *object)
{
return AutoLilvNodes(lilv_world_find_nodes (m_world, subject, predicate, object));
}
// unused + untested yet
bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr)
{

View File

@@ -0,0 +1,93 @@
/*
* Lv2Options.cpp - Lv2Options 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 "Lv2Options.h"
#ifdef LMMS_HAVE_LV2
#include <QtGlobal>
std::set<LV2_URID> Lv2Options::s_supportedOptions;
bool Lv2Options::isOptionSupported(LV2_URID key)
{
return s_supportedOptions.find(key) != s_supportedOptions.end();
}
void Lv2Options::supportOption(LV2_URID key)
{
const auto result = s_supportedOptions.insert(key);
Q_ASSERT(result.second);
}
void Lv2Options::createOptionVectors()
{
// create vector of options
for(LV2_URID urid : s_supportedOptions)
{
auto itr = m_optionByUrid.find(urid);
Q_ASSERT(itr != m_optionByUrid.end());
m_options.push_back(itr->second);
}
LV2_Options_Option nullOption;
nullOption.key = 0;
nullOption.value = nullptr;
m_options.push_back(nullOption);
}
void Lv2Options::initOption(LV2_URID key, uint32_t size, LV2_URID type,
std::shared_ptr<void> value,
LV2_Options_Context context, uint32_t subject)
{
Q_ASSERT(isOptionSupported(key));
LV2_Options_Option opt;
opt.key = key;
opt.context = context;
opt.subject = subject;
opt.size = size;
opt.type = type;
opt.value = value.get();
const auto optResult = m_optionByUrid.emplace(key, opt);
const auto valResult = m_optionValues.emplace(key, std::move(value));
Q_ASSERT(optResult.second);
Q_ASSERT(valResult.second);
}
#endif // LMMS_HAVE_LV2

View File

@@ -128,6 +128,23 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
}
}
Lv2Manager* mgr = Engine::getLv2Manager();
AutoLilvNode requiredOptionNode(mgr->uri(LV2_OPTIONS__requiredOption));
AutoLilvNodes requiredOptions = mgr->findNodes(lilv_plugin_get_uri (plugin), requiredOptionNode.get(), nullptr);
if (requiredOptions)
{
LILV_FOREACH(nodes, i, requiredOptions.get())
{
const char* ro = lilv_node_as_uri (lilv_nodes_get (requiredOptions.get(), i));
if (!Lv2Options::isOptionSupported(mgr->uridMap().map(ro)))
{
// yes, this is not a Lv2 feature,
// but it's a feature in abstract sense
issues.emplace_back(featureNotSupported, ro);
}
}
}
return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2)
? Plugin::Undefined
: (audioChannels[inCount] > 0)
@@ -422,11 +439,38 @@ bool Lv2Proc::hasNoteInput() const
void Lv2Proc::initMOptions()
{
/*
sampleRate:
LMMS can in theory inform plugins of a new sample rate.
However, Lv2 plugins seem to not allow sample rate changes
(not even through LV2_Options_Interface) - it's assumed to be
fixed after being passed via LV2_Descriptor::instantiate.
So, if the sampleRate would change, the plugin will need to
re-initialize, and this code section will be
executed again, creating a new option vector.
*/
float sampleRate = Engine::mixer()->processingSampleRate();
int32_t blockLength = Engine::mixer()->framesPerPeriod();
int32_t sequenceSize = defaultEvbufSize();
using Id = Lv2UridCache::Id;
m_options.initOption<float>(Id::param_sampleRate, sampleRate);
m_options.initOption<int32_t>(Id::bufsz_maxBlockLength, blockLength);
m_options.initOption<int32_t>(Id::bufsz_minBlockLength, blockLength);
m_options.initOption<int32_t>(Id::bufsz_nominalBlockLength, blockLength);
m_options.initOption<int32_t>(Id::bufsz_sequenceSize, sequenceSize);
m_options.createOptionVectors();
}
void Lv2Proc::initPluginSpecificFeatures()
{
// nothing yet
// it would look like this:
// m_features[LV2_URID__map] = m_uridMapFeature
initMOptions();
m_features[LV2_OPTIONS__options] = const_cast<LV2_Options_Option*>(m_options.feature());
}
@@ -558,7 +602,7 @@ void Lv2Proc::createPort(std::size_t portNum)
}
}
int minimumSize = minimumEvbufSize();
int minimumSize = defaultEvbufSize();
Lv2Manager* mgr = Engine::getLv2Manager();

View File

@@ -26,11 +26,19 @@
#ifdef LMMS_HAVE_LV2
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
#include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
#include <lv2/lv2plug.in/ns/ext/parameters/parameters.h>
#include <QtGlobal>
#include "Lv2UridMap.h"
// support newer URIs on old systems
#ifndef LV2_BUF_SIZE__nominalBlockLength
#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength"
#endif
uint32_t Lv2UridCache::operator[](Lv2UridCache::Id id) const
{
Q_ASSERT(id != Id::size);
@@ -47,7 +55,14 @@ Lv2UridCache::Lv2UridCache(UridMap &mapper)
m_cache[static_cast<std::size_t>(id)] = mapper.map(uridStr);
};
init(Id::atom_Float, LV2_ATOM__Float);
init(Id::atom_Int, LV2_ATOM__Int);
init(Id::bufsz_minBlockLength, LV2_BUF_SIZE__minBlockLength);
init(Id::bufsz_maxBlockLength, LV2_BUF_SIZE__maxBlockLength);
init(Id::bufsz_nominalBlockLength, LV2_BUF_SIZE__nominalBlockLength);
init(Id::bufsz_sequenceSize, LV2_BUF_SIZE__sequenceSize);
init(Id::midi_MidiEvent, LV2_MIDI__MidiEvent);
init(Id::param_sampleRate, LV2_PARAMETERS__sampleRate);
for(uint32_t urid : m_cache) { Q_ASSERT(urid != noIdYet); }
}