Skip to content

Commit

Permalink
Enable Lv2 Atom ports
Browse files Browse the repository at this point in the history
All Atom ports are now being created and connected. Currently only MIDI
input ports are supplied with data.

Major contribution to LMMS#562 ("lv2 support").
  • Loading branch information
JohannesLorenz committed Oct 17, 2020
1 parent 9db671c commit 3227be9
Show file tree
Hide file tree
Showing 16 changed files with 800 additions and 31 deletions.
9 changes: 8 additions & 1 deletion include/Lv2ControlBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,12 @@ class Lv2ControlBase : public LinkedModelGroups
/*
utils for the run thread
*/
//! Copy values from all connected models into the respective ports
//! Copy values from the LMMS core (connected models, MIDI events, ...) into
//! the respective ports
void copyModelsFromLmms();
//! Bring values from all ports to the LMMS core
void copyModelsToLmms() const;

//! Copy buffer passed by LMMS into our ports
void copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames);
//! Copy our ports into buffers passed by LMMS
Expand All @@ -123,6 +127,9 @@ class Lv2ControlBase : public LinkedModelGroups
*/
std::size_t controlCount() const;
QString nodeName() const { return "lv2controls"; }
bool hasNoteInput() const;
void handleMidiInputEvent(const class MidiEvent &event,
const class MidiTime &time, f_cnt_t offset);

private:
//! Return the DataFile settings type
Expand Down
149 changes: 149 additions & 0 deletions include/Lv2Evbuf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* lv2_evbuf.h - Lv2 event buffer definitions
*
* 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>
* Original version: 6f22ee0 from https://github.com/drobilla/jalv.git
* Minor changes have been done, but no functional changes.
* Considering this as an "external library", the identifiers do not need to
* match the LMMS coding conventions.
*/

#ifndef LV2_EVBUF_H
#define LV2_EVBUF_H

#include "lmmsconfig.h"

#ifdef LMMS_HAVE_LV2

#include <cstdint>

/**
An abstract/opaque LV2 event buffer.
*/
typedef struct LV2_Evbuf_Impl LV2_Evbuf;

/**
An iterator over an LV2_Evbuf.
*/
typedef struct {
LV2_Evbuf* evbuf;
uint32_t offset;
} LV2_Evbuf_Iterator;

/**
Allocate a new, empty event buffer.
URIDs for atom:Chunk and atom:Sequence must be passed for LV2_EVBUF_ATOM.
*/
LV2_Evbuf*
lv2_evbuf_new(uint32_t capacity, uint32_t atom_Chunk, uint32_t atom_Sequence);

/**
Free an event buffer allocated with lv2_evbuf_new.
*/
void
lv2_evbuf_free(LV2_Evbuf* evbuf);

/**
Clear and initialize an existing event buffer.
The contents of buf are ignored entirely and overwritten, except capacity
which is unmodified.
If input is false and this is an atom buffer, the buffer will be prepared
for writing by the plugin. This MUST be called before every run cycle.
*/
void
lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input);

/**
Return the total padded size of the events stored in the buffer.
*/
uint32_t
lv2_evbuf_get_size(LV2_Evbuf* evbuf);

/**
Return the actual buffer implementation.
The format of the buffer returned depends on the buffer type.
*/
void*
lv2_evbuf_get_buffer(LV2_Evbuf* evbuf);

/**
Return an iterator to the start of `evbuf`.
*/
LV2_Evbuf_Iterator
lv2_evbuf_begin(LV2_Evbuf* evbuf);

/**
Return an iterator to the end of `evbuf`.
*/
LV2_Evbuf_Iterator
lv2_evbuf_end(LV2_Evbuf* evbuf);

/**
Check if `iter` is valid.
@return True if `iter` is valid, otherwise false (past end of buffer)
*/
bool
lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter);

/**
Advance `iter` forward one event.
`iter` must be valid.
@return True if `iter` is valid, otherwise false (reached end of buffer)
*/
LV2_Evbuf_Iterator
lv2_evbuf_next(LV2_Evbuf_Iterator iter);

/**
Dereference an event iterator (i.e. get the event currently pointed to).
`iter` must be valid.
`type` Set to the type of the event.
`size` Set to the size of the event.
`data` Set to the contents of the event.
@return True on success.
*/
bool
lv2_evbuf_get( LV2_Evbuf_Iterator iter,
uint32_t* frames,
uint32_t* type,
uint32_t* size,
uint8_t** data);

/**
Write an event at `iter`.
The event (if any) pointed to by `iter` will be overwritten, and `iter`
incremented to point to the following event (i.e. several calls to this
function can be done in sequence without twiddling iter in-between).
@return True if event was written, otherwise false (buffer is full).
*/
bool
lv2_evbuf_write( LV2_Evbuf_Iterator* iter,
uint32_t frames,
uint32_t type,
uint32_t size,
const uint8_t* data);

#endif // LMMS_HAVE_LV2

#endif // LV2_EVBUF_H
6 changes: 5 additions & 1 deletion include/Lv2Manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <lilv/lilv.h>

#include "Lv2Basics.h"
#include "Lv2UridCache.h"
#include "Lv2UridMap.h"
#include "Plugin.h"

Expand Down Expand Up @@ -123,7 +124,7 @@ class Lv2Manager
};

UridMap& uridMap() { return m_uridMap; }
//! Return all
const Lv2UridCache& uridCache() const { return m_uridCache; }
const std::set<const char*, CmpStr>& supportedFeatureURIs() const
{
return m_supportedFeatureURIs;
Expand All @@ -140,6 +141,9 @@ class Lv2Manager
// feature data that are common for all Lv2Proc
UridMap m_uridMap;

// URID cache for fast URID access
Lv2UridCache m_uridCache;

// functions
bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr);
};
Expand Down
23 changes: 22 additions & 1 deletion include/Lv2Ports.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "PluginIssue.h"

struct ConnectPortVisitor;
typedef struct LV2_Evbuf_Impl LV2_Evbuf;

namespace Lv2Ports {

Expand All @@ -53,7 +54,7 @@ enum class Type {
Unknown,
Control,
Audio,
Event, //!< TODO: unused, describe
AtomSeq,
Cv //!< TODO: unused, describe
};

Expand All @@ -74,6 +75,7 @@ struct ControlPortBase;
struct Control;
struct Audio;
struct Cv;
struct AtomSeq;
struct Unknown;

struct ConstVisitor
Expand All @@ -82,6 +84,7 @@ struct ConstVisitor
virtual void visit(const Lv2Ports::Control& ) {}
virtual void visit(const Lv2Ports::Audio& ) {}
virtual void visit(const Lv2Ports::Cv& ) {}
virtual void visit(const Lv2Ports::AtomSeq& ) {}
virtual void visit(const Lv2Ports::Unknown& ) {}

virtual ~ConstVisitor();
Expand All @@ -93,6 +96,7 @@ struct Visitor
virtual void visit(Lv2Ports::Control& ) {}
virtual void visit(Lv2Ports::Audio& ) {}
virtual void visit(Lv2Ports::Cv& ) {}
virtual void visit(Lv2Ports::AtomSeq& ) {}
virtual void visit(Lv2Ports::Unknown& ) {}

virtual ~Visitor();
Expand Down Expand Up @@ -192,6 +196,23 @@ struct Audio : public VisitablePort<Audio, PortBase>
friend struct ::ConnectPortVisitor;
};

struct AtomSeq : public VisitablePort<AtomSeq, PortBase>
{
enum FlagType
{
None = 0,
Midi = 1
};
unsigned flags = FlagType::None;

struct Lv2EvbufDeleter
{
void operator()(LV2_Evbuf* n);
};
using AutoLv2Evbuf = std::unique_ptr<LV2_Evbuf, Lv2EvbufDeleter>;
AutoLv2Evbuf m_buf;
};

struct Unknown : public VisitablePort<Unknown, PortBase>
{
};
Expand Down
31 changes: 29 additions & 2 deletions include/Lv2Proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@
#include "Lv2Basics.h"
#include "Lv2Features.h"
#include "LinkedModelGroups.h"
#include "MidiEvent.h"
#include "MidiTime.h"
#include "Plugin.h"
#include "PluginIssue.h"

#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h"

// forward declare port structs/enums
namespace Lv2Ports
{
struct Audio;
struct PortBase;
struct AtomSeq;

enum class Type;
enum class Flow;
Expand Down Expand Up @@ -106,8 +109,11 @@ class Lv2Proc : public LinkedModelGroup
/*
utils for the run thread
*/
//! Copy values from all connected models into the respective ports
//! Copy values from the LMMS core (connected models, MIDI events, ...) into
//! the respective ports
void copyModelsFromCore();
//! Bring values from all ports to the LMMS core
void copyModelsToCore();
/**
* Copy buffer passed by the core into our ports
* @param buf buffer of sample frames, each sample frame is something like
Expand Down Expand Up @@ -137,11 +143,15 @@ class Lv2Proc : public LinkedModelGroup
//! Run the Lv2 plugin instance for @param frames frames
void run(fpp_t frames);

void handleMidiInputEvent(const class MidiEvent &event,
const MidiTime &time, f_cnt_t offset);

/*
misc
*/
class AutomatableModel *modelAtPort(const QString &uri); // unused currently
std::size_t controlCount() const { return LinkedModelGroup::modelNum(); }
bool hasNoteInput() const;

protected:
/*
Expand All @@ -159,8 +169,25 @@ class Lv2Proc : public LinkedModelGroup
LilvInstance* m_instance;
Lv2Features m_features;

// full list of ports
std::vector<std::unique_ptr<Lv2Ports::PortBase>> m_ports;
// quick reference to specific, unique ports
StereoPortRef m_inPorts, m_outPorts;
Lv2Ports::AtomSeq *m_midiIn = nullptr, *m_midiOut = nullptr;

// MIDI
// many things here may be moved into the `Instrument` class
constexpr const static std::size_t m_maxMidiInputEvents = 1024;
//! spinlock for the MIDI ringbuffer (for MIDI events going to the plugin)
std::atomic_flag m_ringLock = ATOMIC_FLAG_INIT;

//! MIDI ringbuffer (for MIDI events going to the plugin)
ringbuffer_t<struct MidiInputEvent> m_midiInputBuf;
//! MIDI ringbuffer reader
ringbuffer_reader_t<struct MidiInputEvent> m_midiInputReader;

// other
static std::size_t minimumEvbufSize() { return 1 << 15; /* ardour uses this*/ }

//! models for the controls, sorted by port symbols
std::map<std::string, AutomatableModel *> m_connectedModels;
Expand Down
52 changes: 52 additions & 0 deletions include/Lv2UridCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Lv2UridCache.h - Lv2UridCache definition
*
* 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 LV2URIDCACHE_H
#define LV2URIDCACHE_H

#include "lmmsconfig.h"

#ifdef LMMS_HAVE_LV2

#include <cstdint>

//! Cached URIDs for fast access (for use in real-time code)
class Lv2UridCache
{
public:
enum class Id //!< ID for m_uridCache array
{
midi_MidiEvent,
size
};
//! Return URID for a cache ID
uint32_t operator[](Id id) const;

Lv2UridCache(class UridMap& mapper);
private:
uint32_t m_cache[static_cast<int>(Id::size)];
};

#endif // LMMS_HAVE_LV2
#endif // LV2URIDCACHE_H
Loading

0 comments on commit 3227be9

Please sign in to comment.