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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
93
src/core/lv2/Lv2Options.cpp
Normal file
93
src/core/lv2/Lv2Options.cpp
Normal 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
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user