Enable Lv2 Atom ports
All Atom ports are now being created and connected. Currently only MIDI input ports are supplied with data. Major contribution to #562 ("lv2 support").
This commit is contained in:
committed by
Johannes Lorenz
parent
acd0e4d430
commit
8b2902c27a
@@ -93,6 +93,7 @@ set(LMMS_SRCS
|
||||
|
||||
core/lv2/Lv2Basics.cpp
|
||||
core/lv2/Lv2ControlBase.cpp
|
||||
core/lv2/Lv2Evbuf.cpp
|
||||
core/lv2/Lv2Features.cpp
|
||||
core/lv2/Lv2Ports.cpp
|
||||
core/lv2/Lv2Proc.cpp
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <algorithm>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "Engine.h"
|
||||
@@ -113,6 +114,14 @@ void Lv2ControlBase::copyModelsFromLmms() {
|
||||
|
||||
|
||||
|
||||
void Lv2ControlBase::copyModelsToLmms() const
|
||||
{
|
||||
for (auto& c : m_procs) { c->copyModelsToCore(); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) {
|
||||
unsigned firstChan = 0; // tell the procs which channels they shall read from
|
||||
for (auto& c : m_procs) {
|
||||
@@ -187,4 +196,22 @@ std::size_t Lv2ControlBase::controlCount() const {
|
||||
|
||||
|
||||
|
||||
bool Lv2ControlBase::hasNoteInput() const
|
||||
{
|
||||
return std::any_of(m_procs.begin(), m_procs.end(),
|
||||
[](const auto& c) { return c->hasNoteInput(); });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Lv2ControlBase::handleMidiInputEvent(const MidiEvent &event,
|
||||
const MidiTime &time, f_cnt_t offset)
|
||||
{
|
||||
for (auto& c : m_procs) { c->handleMidiInputEvent(event, time, offset); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
|
||||
193
src/core/lv2/Lv2Evbuf.cpp
Normal file
193
src/core/lv2/Lv2Evbuf.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* lv2_evbuf.cpp - Lv2 event buffer implementation
|
||||
*
|
||||
* Copyright (c) 2019-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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The original code was written by David Robillard <http://drobilla.net>
|
||||
*/
|
||||
|
||||
#include "Lv2Evbuf.h"
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
|
||||
|
||||
struct LV2_Evbuf_Impl {
|
||||
uint32_t capacity;
|
||||
uint32_t atom_Chunk;
|
||||
uint32_t atom_Sequence;
|
||||
LV2_Atom_Sequence buf;
|
||||
};
|
||||
|
||||
static inline uint32_t
|
||||
lv2_evbuf_pad_size(uint32_t size)
|
||||
{
|
||||
return (size + 7) & (~7);
|
||||
}
|
||||
|
||||
LV2_Evbuf*
|
||||
lv2_evbuf_new(uint32_t capacity, uint32_t atom_Chunk, uint32_t atom_Sequence)
|
||||
{
|
||||
// FIXME: memory must be 64-bit aligned
|
||||
LV2_Evbuf* evbuf = (LV2_Evbuf*)malloc(
|
||||
sizeof(LV2_Evbuf) + sizeof(LV2_Atom_Sequence) + capacity);
|
||||
evbuf->capacity = capacity;
|
||||
evbuf->atom_Chunk = atom_Chunk;
|
||||
evbuf->atom_Sequence = atom_Sequence;
|
||||
lv2_evbuf_reset(evbuf, true);
|
||||
return evbuf;
|
||||
}
|
||||
|
||||
void
|
||||
lv2_evbuf_free(LV2_Evbuf* evbuf)
|
||||
{
|
||||
free(evbuf);
|
||||
}
|
||||
|
||||
void
|
||||
lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input)
|
||||
{
|
||||
if (input) {
|
||||
evbuf->buf.atom.size = sizeof(LV2_Atom_Sequence_Body);
|
||||
evbuf->buf.atom.type = evbuf->atom_Sequence;
|
||||
} else {
|
||||
evbuf->buf.atom.size = evbuf->capacity;
|
||||
evbuf->buf.atom.type = evbuf->atom_Chunk;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
lv2_evbuf_get_size(LV2_Evbuf* evbuf)
|
||||
{
|
||||
assert(evbuf->buf.atom.type != evbuf->atom_Sequence
|
||||
|| evbuf->buf.atom.size >= sizeof(LV2_Atom_Sequence_Body));
|
||||
return evbuf->buf.atom.type == evbuf->atom_Sequence
|
||||
? evbuf->buf.atom.size - sizeof(LV2_Atom_Sequence_Body)
|
||||
: 0;
|
||||
}
|
||||
|
||||
void*
|
||||
lv2_evbuf_get_buffer(LV2_Evbuf* evbuf)
|
||||
{
|
||||
return &evbuf->buf;
|
||||
}
|
||||
|
||||
LV2_Evbuf_Iterator
|
||||
lv2_evbuf_begin(LV2_Evbuf* evbuf)
|
||||
{
|
||||
LV2_Evbuf_Iterator iter = { evbuf, 0 };
|
||||
return iter;
|
||||
}
|
||||
|
||||
LV2_Evbuf_Iterator
|
||||
lv2_evbuf_end(LV2_Evbuf* evbuf)
|
||||
{
|
||||
const uint32_t size = lv2_evbuf_get_size(evbuf);
|
||||
const LV2_Evbuf_Iterator iter = { evbuf, lv2_evbuf_pad_size(size) };
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool
|
||||
lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter)
|
||||
{
|
||||
return iter.offset < lv2_evbuf_get_size(iter.evbuf);
|
||||
}
|
||||
|
||||
LV2_Evbuf_Iterator
|
||||
lv2_evbuf_next(LV2_Evbuf_Iterator iter)
|
||||
{
|
||||
if (!lv2_evbuf_is_valid(iter)) {
|
||||
return iter;
|
||||
}
|
||||
|
||||
LV2_Evbuf* evbuf = iter.evbuf;
|
||||
uint32_t offset = iter.offset;
|
||||
uint32_t size;
|
||||
size = ((LV2_Atom_Event*)
|
||||
((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, &evbuf->buf.atom)
|
||||
+ offset))->body.size;
|
||||
offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
|
||||
|
||||
LV2_Evbuf_Iterator next = { evbuf, offset };
|
||||
return next;
|
||||
}
|
||||
|
||||
bool
|
||||
lv2_evbuf_get(LV2_Evbuf_Iterator iter,
|
||||
uint32_t* frames,
|
||||
uint32_t* type,
|
||||
uint32_t* size,
|
||||
uint8_t** data)
|
||||
{
|
||||
*frames = *type = *size = 0;
|
||||
*data = NULL;
|
||||
|
||||
if (!lv2_evbuf_is_valid(iter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LV2_Atom_Sequence* aseq = &iter.evbuf->buf;
|
||||
LV2_Atom_Event* aev = (LV2_Atom_Event*)(
|
||||
(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter.offset);
|
||||
|
||||
*frames = aev->time.frames;
|
||||
*type = aev->body.type;
|
||||
*size = aev->body.size;
|
||||
*data = (uint8_t*)LV2_ATOM_BODY(&aev->body);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
|
||||
uint32_t frames,
|
||||
uint32_t type,
|
||||
uint32_t size,
|
||||
const uint8_t* data)
|
||||
{
|
||||
LV2_Atom_Sequence* aseq = &iter->evbuf->buf;
|
||||
if (iter->evbuf->capacity - sizeof(LV2_Atom) - aseq->atom.size <
|
||||
sizeof(LV2_Atom_Event) + size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LV2_Atom_Event* aev = (LV2_Atom_Event*)(
|
||||
(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter->offset);
|
||||
|
||||
aev->time.frames = frames;
|
||||
aev->body.type = type;
|
||||
aev->body.size = size;
|
||||
memcpy(LV2_ATOM_BODY(&aev->body), data, size);
|
||||
|
||||
size = lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
|
||||
aseq->atom.size += size;
|
||||
iter->offset += size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
@@ -27,9 +27,12 @@
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Lv2Basics.h"
|
||||
#include "Lv2Manager.h"
|
||||
#include "Lv2Evbuf.h"
|
||||
|
||||
namespace Lv2Ports {
|
||||
|
||||
@@ -57,7 +60,7 @@ const char *toStr(Type pt)
|
||||
case Type::Unknown: return "unknown";
|
||||
case Type::Control: return "control";
|
||||
case Type::Audio: return "audio";
|
||||
case Type::Event: return "event";
|
||||
case Type::AtomSeq: return "atom-sequence";
|
||||
case Type::Cv: return "cv";
|
||||
}
|
||||
return "";
|
||||
@@ -168,6 +171,23 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
issue(badPortType, "cvPort");
|
||||
m_type = Type::Cv;
|
||||
}
|
||||
else if (isA(LV2_ATOM__AtomPort))
|
||||
{
|
||||
AutoLilvNode uriAtomSequence(Engine::getLv2Manager()->uri(LV2_ATOM__Sequence));
|
||||
AutoLilvNode uriAtomBufferType(Engine::getLv2Manager()->uri(LV2_ATOM__bufferType));
|
||||
AutoLilvNodes bufferTypes(lilv_port_get_value(plugin, lilvPort, uriAtomBufferType.get()));
|
||||
|
||||
if (lilv_nodes_contains(bufferTypes.get(), uriAtomSequence.get()))
|
||||
{
|
||||
// we accept all kinds of atom sequence ports, even if they take or
|
||||
// offer atom types that we do not support:
|
||||
// * atom input ports only say what *can* be input, but not what is
|
||||
// required as input
|
||||
// * atom output ports only say what *can* be output, but not what must
|
||||
// be evaluated
|
||||
m_type = Type::AtomSeq;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_type == Type::Unknown)
|
||||
{
|
||||
@@ -245,6 +265,11 @@ void Audio::copyBuffersToCore(sampleFrame *lmmsBuf,
|
||||
|
||||
|
||||
|
||||
void AtomSeq::Lv2EvbufDeleter::operator()(LV2_Evbuf *n) { lv2_evbuf_free(n); }
|
||||
|
||||
|
||||
|
||||
|
||||
// make the compiler happy, give each class with virtuals
|
||||
// a function (the destructor here) which is in a cpp file
|
||||
PortBase::~PortBase() {}
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <cmath>
|
||||
#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/resize-port/resize-port.h>
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "AutomatableModel.h"
|
||||
#include "ComboBoxModel.h"
|
||||
@@ -34,17 +39,31 @@
|
||||
#include "Lv2Features.h"
|
||||
#include "Lv2Manager.h"
|
||||
#include "Lv2Ports.h"
|
||||
#include "Lv2Evbuf.h"
|
||||
#include "MidiEventToByteSeq.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// container for everything required to store MIDI events going to the plugin
|
||||
struct MidiInputEvent
|
||||
{
|
||||
MidiEvent ev;
|
||||
MidiTime time;
|
||||
f_cnt_t offset;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
std::vector<PluginIssue>& issues, bool printIssues)
|
||||
{
|
||||
unsigned maxPorts = lilv_plugin_get_num_ports(plugin);
|
||||
enum { inCount, outCount, maxCount };
|
||||
unsigned audioChannels[maxCount] = { 0, 0 }; // input and output count
|
||||
unsigned audioChannels[maxCount] = { 0, 0 }; // audio input and output count
|
||||
unsigned midiChannels[maxCount] = { 0, 0 }; // MIDI input and output count
|
||||
|
||||
for (unsigned portNum = 0; portNum < maxPorts; ++portNum)
|
||||
{
|
||||
@@ -58,8 +77,15 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
lilv_plugin_get_port_by_index(plugin, portNum)) &&
|
||||
!meta.m_optional;
|
||||
if (meta.m_type == Lv2Ports::Type::Audio && portMustBeUsed)
|
||||
{
|
||||
++audioChannels[meta.m_flow == Lv2Ports::Flow::Output
|
||||
? outCount : inCount];
|
||||
}
|
||||
else if(meta.m_type == Lv2Ports::Type::AtomSeq && portMustBeUsed)
|
||||
{
|
||||
++midiChannels[meta.m_flow == Lv2Ports::Flow::Output
|
||||
? outCount : inCount];
|
||||
}
|
||||
}
|
||||
|
||||
if (audioChannels[inCount] > 2)
|
||||
@@ -71,6 +97,13 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
issues.emplace_back(tooManyOutputChannels,
|
||||
std::to_string(audioChannels[outCount]));
|
||||
|
||||
if (midiChannels[inCount] > 1)
|
||||
issues.emplace_back(tooManyMidiInputChannels,
|
||||
std::to_string(midiChannels[inCount]));
|
||||
if (midiChannels[outCount] > 1)
|
||||
issues.emplace_back(tooManyMidiOutputChannels,
|
||||
std::to_string(midiChannels[outCount]));
|
||||
|
||||
AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin));
|
||||
LILV_FOREACH (nodes, itr, reqFeats.get())
|
||||
{
|
||||
@@ -104,7 +137,9 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
|
||||
Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) :
|
||||
LinkedModelGroup(parent),
|
||||
m_plugin(plugin)
|
||||
m_plugin(plugin),
|
||||
m_midiInputBuf(m_maxMidiInputEvents),
|
||||
m_midiInputReader(m_midiInputBuf)
|
||||
{
|
||||
initPlugin();
|
||||
}
|
||||
@@ -149,29 +184,79 @@ void Lv2Proc::copyModelsFromCore()
|
||||
{
|
||||
void visit(Lv2Ports::Control& ctrl) override
|
||||
{
|
||||
if (ctrl.m_flow == Lv2Ports::Flow::Input)
|
||||
{
|
||||
FloatFromModelVisitor ffm;
|
||||
ffm.m_scalePointMap = &ctrl.m_scalePointMap;
|
||||
ctrl.m_connectedModel->accept(ffm);
|
||||
ctrl.m_val = ffm.m_res;
|
||||
}
|
||||
FloatFromModelVisitor ffm;
|
||||
ffm.m_scalePointMap = &ctrl.m_scalePointMap;
|
||||
ctrl.m_connectedModel->accept(ffm);
|
||||
ctrl.m_val = ffm.m_res;
|
||||
}
|
||||
void visit(Lv2Ports::Cv& cv) override
|
||||
{
|
||||
if (cv.m_flow == Lv2Ports::Flow::Input)
|
||||
{
|
||||
FloatFromModelVisitor ffm;
|
||||
ffm.m_scalePointMap = &cv.m_scalePointMap;
|
||||
cv.m_connectedModel->accept(ffm);
|
||||
// dirty fix, needs better interpolation
|
||||
std::fill(cv.m_buffer.begin(), cv.m_buffer.end(), ffm.m_res);
|
||||
}
|
||||
FloatFromModelVisitor ffm;
|
||||
ffm.m_scalePointMap = &cv.m_scalePointMap;
|
||||
cv.m_connectedModel->accept(ffm);
|
||||
// dirty fix, needs better interpolation
|
||||
std::fill(cv.m_buffer.begin(), cv.m_buffer.end(), ffm.m_res);
|
||||
}
|
||||
void visit(Lv2Ports::AtomSeq& atomPort) override
|
||||
{
|
||||
lv2_evbuf_reset(atomPort.m_buf.get(), true);
|
||||
}
|
||||
} copy;
|
||||
|
||||
for (const std::unique_ptr<Lv2Ports::PortBase>& port : m_ports) {
|
||||
port->accept(copy); }
|
||||
// feed each input port with the respective data from the LMMS core
|
||||
for (const std::unique_ptr<Lv2Ports::PortBase>& port : m_ports)
|
||||
{
|
||||
if (port->m_flow == Lv2Ports::Flow::Input)
|
||||
{
|
||||
port->accept(copy);
|
||||
}
|
||||
}
|
||||
|
||||
// send pending MIDI events to atom port
|
||||
if(m_midiIn)
|
||||
{
|
||||
LV2_Evbuf_Iterator iter = lv2_evbuf_begin(m_midiIn->m_buf.get());
|
||||
// MIDI events waiting to go to the plugin?
|
||||
while(m_midiInputReader.read_space() > 0)
|
||||
{
|
||||
const MidiInputEvent ev = m_midiInputReader.read(1)[0];
|
||||
uint32_t atomStamp =
|
||||
ev.time.frames(Engine::framesPerTick()) + ev.offset;
|
||||
uint32_t type = Engine::getLv2Manager()->
|
||||
uridCache()[Lv2UridCache::Id::midi_MidiEvent];
|
||||
uint8_t buf[4];
|
||||
std::size_t bufsize = writeToByteSeq(ev.ev, buf, sizeof(buf));
|
||||
if(bufsize)
|
||||
{
|
||||
lv2_evbuf_write(&iter, atomStamp, type, bufsize, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Lv2Proc::copyModelsToCore()
|
||||
{
|
||||
struct Copy : public Lv2Ports::Visitor
|
||||
{
|
||||
void visit(Lv2Ports::AtomSeq& atomPort) override
|
||||
{
|
||||
// we currently don't copy anything, but we need to clear the buffer
|
||||
// for the plugin to write again
|
||||
lv2_evbuf_reset(atomPort.m_buf.get(), false);
|
||||
}
|
||||
} copy;
|
||||
|
||||
// fetch data from each output port and bring it to the LMMS core
|
||||
for (const std::unique_ptr<Lv2Ports::PortBase>& port : m_ports)
|
||||
{
|
||||
if (port->m_flow == Lv2Ports::Flow::Output)
|
||||
{
|
||||
port->accept(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -229,6 +314,41 @@ void Lv2Proc::run(fpp_t frames)
|
||||
|
||||
|
||||
|
||||
// in case there will be a PR which removes this callback and instead adds a
|
||||
// `ringbuffer_t<MidiEvent + time info>` to `class Instrument`, this
|
||||
// function (and the ringbuffer and its reader in `Lv2Proc`) will simply vanish
|
||||
void Lv2Proc::handleMidiInputEvent(const MidiEvent &event, const MidiTime &time, f_cnt_t offset)
|
||||
{
|
||||
if(m_midiIn)
|
||||
{
|
||||
// ringbuffer allows only one writer at a time
|
||||
// however, this function can be called by multiple threads
|
||||
// (different RT and non-RT!) at the same time
|
||||
// for now, a spinlock looks like the most safe/easy compromise
|
||||
|
||||
// source: https://en.cppreference.com/w/cpp/atomic/atomic_flag
|
||||
while (m_ringLock.test_and_set(std::memory_order_acquire)) // acquire lock
|
||||
; // spin
|
||||
|
||||
MidiInputEvent ev { event, time, offset };
|
||||
std::size_t written = m_midiInputBuf.write(&ev, 1);
|
||||
if(written != 1)
|
||||
{
|
||||
qWarning("MIDI ringbuffer is too small! Discarding MIDI event.");
|
||||
}
|
||||
|
||||
m_ringLock.clear(std::memory_order_release);
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Warning: Caught MIDI event for an Lv2 instrument"
|
||||
<< "that can not hande MIDI... Ignoring";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
AutomatableModel *Lv2Proc::modelAtPort(const QString &uri)
|
||||
{
|
||||
// unused currently
|
||||
@@ -284,6 +404,18 @@ void Lv2Proc::shutdownPlugin()
|
||||
|
||||
|
||||
|
||||
bool Lv2Proc::hasNoteInput() const
|
||||
{
|
||||
return m_midiIn;
|
||||
// we could additionally check for
|
||||
// http://lv2plug.in/ns/lv2core#InstrumentPlugin
|
||||
// however, jalv does not do that, too
|
||||
// so, if there's any MIDI input, we just assume we can send notes there
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Lv2Proc::initPluginSpecificFeatures()
|
||||
{
|
||||
// nothing yet
|
||||
@@ -385,6 +517,46 @@ void Lv2Proc::createPort(std::size_t portNum)
|
||||
port = audio;
|
||||
break;
|
||||
}
|
||||
case Lv2Ports::Type::AtomSeq:
|
||||
{
|
||||
Lv2Ports::AtomSeq* atomPort = new Lv2Ports::AtomSeq;
|
||||
|
||||
{
|
||||
AutoLilvNode uriAtomSupports(Engine::getLv2Manager()->uri(LV2_ATOM__supports));
|
||||
AutoLilvNodes atomSupports(lilv_port_get_value(m_plugin, lilvPort, uriAtomSupports.get()));
|
||||
AutoLilvNode uriMidiEvent(Engine::getLv2Manager()->uri(LV2_MIDI__MidiEvent));
|
||||
|
||||
LILV_FOREACH (nodes, itr, atomSupports.get())
|
||||
{
|
||||
if(lilv_node_equals(lilv_nodes_get(atomSupports.get(), itr), uriMidiEvent.get()))
|
||||
{
|
||||
atomPort->flags |= Lv2Ports::AtomSeq::FlagType::Midi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int minimumSize = minimumEvbufSize();
|
||||
|
||||
Lv2Manager* mgr = Engine::getLv2Manager();
|
||||
|
||||
// check for alternative minimum size
|
||||
{
|
||||
AutoLilvNode rszMinimumSize = mgr->uri(LV2_RESIZE_PORT__minimumSize);
|
||||
AutoLilvNodes minSizeV(lilv_port_get_value(m_plugin, lilvPort, rszMinimumSize.get()));
|
||||
LilvNode* minSize = minSizeV ? lilv_nodes_get_first(minSizeV.get()) : nullptr;
|
||||
if (minSize && lilv_node_is_int(minSize)) {
|
||||
minimumSize = std::max(minimumSize, lilv_node_as_int(minSize));
|
||||
}
|
||||
}
|
||||
|
||||
atomPort->m_buf.reset(
|
||||
lv2_evbuf_new(static_cast<uint32_t>(minimumSize),
|
||||
mgr->uridMap().map(LV2_ATOM__Chunk),
|
||||
mgr->uridMap().map(LV2_ATOM__Sequence)));
|
||||
|
||||
port = atomPort;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
port = new Lv2Ports::Unknown;
|
||||
}
|
||||
@@ -445,6 +617,33 @@ void Lv2Proc::createPorts()
|
||||
else if (!portRef->m_right) { portRef->m_right = &audio; }
|
||||
}
|
||||
}
|
||||
|
||||
void visit(Lv2Ports::AtomSeq& atomPort) override
|
||||
{
|
||||
if(atomPort.m_flow == Lv2Ports::Flow::Input)
|
||||
{
|
||||
if(atomPort.flags & Lv2Ports::AtomSeq::FlagType::Midi)
|
||||
{
|
||||
// take any MIDI input, prefer mandatory MIDI input
|
||||
// (Lv2Proc::check() assures there are <=1 mandatory MIDI
|
||||
// input ports)
|
||||
if(!m_proc->m_midiIn || !atomPort.m_optional)
|
||||
m_proc->m_midiIn = &atomPort;
|
||||
}
|
||||
}
|
||||
else if(atomPort.m_flow == Lv2Ports::Flow::Output)
|
||||
{
|
||||
if(atomPort.flags & Lv2Ports::AtomSeq::FlagType::Midi)
|
||||
{
|
||||
// take any MIDI output, prefer mandatory MIDI output
|
||||
// (Lv2Proc::check() assures there are <=1 mandatory MIDI
|
||||
// output ports)
|
||||
if(!m_proc->m_midiOut || !atomPort.m_optional)
|
||||
m_proc->m_midiOut = &atomPort;
|
||||
}
|
||||
}
|
||||
else { Q_ASSERT(false); }
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t maxPorts = lilv_plugin_get_num_ports(m_plugin);
|
||||
@@ -472,10 +671,15 @@ struct ConnectPortVisitor : public Lv2Ports::Visitor
|
||||
{
|
||||
std::size_t m_num;
|
||||
LilvInstance* m_instance;
|
||||
void connectPort(void* location) {
|
||||
void connectPort(void* location)
|
||||
{
|
||||
lilv_instance_connect_port(m_instance,
|
||||
static_cast<uint32_t>(m_num), location);
|
||||
}
|
||||
void visit(Lv2Ports::AtomSeq& atomSeq) override
|
||||
{
|
||||
connectPort(lv2_evbuf_get_buffer(atomSeq.m_buf.get()));
|
||||
}
|
||||
void visit(Lv2Ports::Control& ctrl) override { connectPort(&ctrl.m_val); }
|
||||
void visit(Lv2Ports::Audio& audio) override
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user