Skip to content

Commit

Permalink
Lv2 core implementation
Browse files Browse the repository at this point in the history
Implementation of the Lv2 core, except for CV ports. No features or
extensions are supported yet.

You can now generate sound using Lv2 instruments (restricted to non-piano)
or effects.

For an explenation about the new classes, see Lv2Manager.h
  • Loading branch information
JohannesLorenz committed May 24, 2020
1 parent 7aef23d commit 2a66e83
Show file tree
Hide file tree
Showing 39 changed files with 3,536 additions and 3 deletions.
5 changes: 4 additions & 1 deletion .travis/linux..install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ SWH_PACKAGES="perl libxml2-utils libxml-perl liblist-moreutils-perl"
# VST dependencies
VST_PACKAGES="wine-dev qt59x11extras qtbase5-private-dev libxcb-util0-dev libxcb-keysyms1-dev"

# LV2 dependencies; libsuil-dev is not required
LV2_PACKAGES="lv2-dev liblilv-dev"

# Help with unmet dependencies
PACKAGES="$PACKAGES $SWH_PACKAGES $VST_PACKAGES libjack-jackd2-0"
PACKAGES="$PACKAGES $SWH_PACKAGES $VST_PACKAGES $LV2_PACKAGES libjack-jackd2-0"

# shellcheck disable=SC2086
sudo apt-get install -y $PACKAGES
37 changes: 37 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ OPTION(WANT_CARLA "Include Carla plugin" ON)
OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON)
OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON)
OPTION(WANT_WEAKJACK "Loosely link JACK libraries" ON)
OPTION(WANT_LV2 "Include Lv2 plugins" ON)
OPTION(WANT_SUIL "Include SUIL for LV2 plugin UIs" ON)
OPTION(WANT_MP3LAME "Include MP3/Lame support" ON)
OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON)
OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON)
Expand Down Expand Up @@ -182,6 +184,39 @@ IF(NOT SNDFILE_VERSION VERSION_LESS 1.0.26)
SET(LMMS_HAVE_SF_COMPLEVEL TRUE)
ENDIF()

IF(WANT_LV2)
IF(PKG_CONFIG_FOUND)
PKG_CHECK_MODULES(LV2 lv2)
PKG_CHECK_MODULES(LILV lilv-0)
IF(LV2_FOUND AND LILV_FOUND)
SET(LMMS_HAVE_LV2 TRUE)
SET(STATUS_LV2 "OK")
ELSE()
SET(STATUS_LV2 "not found, install it or set PKG_CONFIG_PATH appropriately")
ENDIF()
ELSE()
SET(STATUS_LV2 "not found, requires pkg-config")
ENDIF()
ELSE(WANT_LV2)
SET(STATUS_LV2 "not built as requested")
ENDIF(WANT_LV2)

IF(WANT_SUIL)
IF(PKG_CONFIG_FOUND)
PKG_CHECK_MODULES(SUIL suil-0)
IF(SUIL_FOUND)
SET(LMMS_HAVE_SUIL TRUE)
SET(STATUS_SUIL "OK")
ELSE()
SET(STATUS_SUIL "not found, install it or set PKG_CONFIG_PATH appropriately")
ENDIF()
ELSE()
SET(STATUS_SUIL "not found, requires pkg-config")
ENDIF()
ELSE(WANT_SUIL)
SET(STATUS_SUIL "not built as requested")
ENDIF(WANT_SUIL)

IF(WANT_CALF)
SET(LMMS_HAVE_CALF TRUE)
SET(STATUS_CALF "OK")
Expand Down Expand Up @@ -686,6 +721,8 @@ MESSAGE(
MESSAGE(
"Optional plugins\n"
"----------------\n"
"* Lv2 plugins : ${STATUS_LV2}\n"
"* SUIL for plugin UIs : ${STATUS_SUIL}\n"
"* ZynAddSubFX instrument : ${STATUS_ZYN}\n"
"* Carla Patchbay & Rack : ${STATUS_CARLA}\n"
"* SoundFont2 player : ${STATUS_FLUIDSYNTH}\n"
Expand Down
2 changes: 2 additions & 0 deletions cmake/modules/PluginList.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ SET(LMMS_PLUGIN_LIST
HydrogenImport
ladspa_browser
LadspaEffect
Lv2Effect
Lv2Instrument
lb302
MidiImport
MidiExport
Expand Down
11 changes: 11 additions & 0 deletions include/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <QtCore/QObject>


#include "lmmsconfig.h"
#include "lmms_export.h"
#include "lmms_basics.h"

Expand Down Expand Up @@ -87,6 +88,13 @@ class LMMS_EXPORT LmmsCore : public QObject
return s_projectJournal;
}

#ifdef LMMS_HAVE_LV2
static class Lv2Manager * getLv2Manager()
{
return s_lv2Manager;
}
#endif

static Ladspa2LMMS * getLADSPAManager()
{
return s_ladspaManager;
Expand Down Expand Up @@ -143,6 +151,9 @@ class LMMS_EXPORT LmmsCore : public QObject
static ProjectJournal * s_projectJournal;
static DummyTrackContainer * s_dummyTC;

#ifdef LMMS_HAVE_LV2
static class Lv2Manager* s_lv2Manager;
#endif
static Ladspa2LMMS * s_ladspaManager;
static void* s_dndPluginKey;

Expand Down
67 changes: 67 additions & 0 deletions include/Lv2Basics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Lv2Basics.h - basic Lv2 utils
*
* Copyright (c) 2018-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 LV2BASICS_H
#define LV2BASICS_H


#include "lmmsconfig.h"

#ifdef LMMS_HAVE_LV2

#include <lilv/lilv.h>
#include <memory>
#include <QString>
#include <string>

struct LilvNodeDeleter
{
void operator()(LilvNode* n) { lilv_node_free(n); }
};

struct LilvNodesDeleter
{
void operator()(LilvNodes* n) { lilv_nodes_free(n); }
};

using AutoLilvNode = std::unique_ptr<LilvNode, LilvNodeDeleter>;
using AutoLilvNodes = std::unique_ptr<LilvNodes, LilvNodesDeleter>;

/**
Return QString from a plugin's node, everything will be freed automatically
@param plug The plugin where the node is
@param getFunc The function to return the node from the plugin
*/
QString qStringFromPluginNode(const LilvPlugin* plug,
LilvNode * (*getFunc)(const LilvPlugin*));

//! Return port name as QString, everything will be freed automatically
QString qStringFromPortName(const LilvPlugin* plug, const LilvPort* port);

//! Return port name as std::string, everything will be freed automatically
std::string stdStringFromPortName(const LilvPlugin* plug, const LilvPort* port);

#endif // LMMS_HAVE_LV2
#endif // LV2BASICS_H
146 changes: 146 additions & 0 deletions include/Lv2ControlBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Lv2ControlBase.h - Lv2 control base class
*
* Copyright (c) 2018-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 LV2_CONTROL_BASE_H
#define LV2_CONTROL_BASE_H

#include "lmmsconfig.h"

#ifdef LMMS_HAVE_LV2

#include <lilv/lilv.h>

#include "DataFile.h"
#include "LinkedModelGroups.h"
#include "Plugin.h"

class Lv2Proc;
class PluginIssue;

/**
Common base class for Lv2 plugins
This class contains a vector of Lv2Proc, usually 1 (for stereo plugins) or
2 (for mono plugins). Most of the logic is done there, this class primarily
forwards work to the Lv2Proc and collects the results.
This class provides everything Lv2 plugins have in common. It's not
named Lv2Plugin, because
* it does not inherit Instrument
* the Plugin subclass Effect does not inherit this class
This class would usually be a Model subclass. However, Qt doesn't allow
this:
* inhertiting only from Model will cause diamond inheritance for QObject,
which will cause errors with Q_OBJECT
* making this a direct subclass of Instrument resp. EffectControls would
require CRTP, which would make this class a template class, which would
conflict with Q_OBJECT
The consequence is that this class can neither inherit QObject or Model, nor
Instrument or EffectControls, which means in fact:
* this class contains no signals or slots, but it offers stubs for slots
that shall be called by child classes
* this class can not override virtuals of Instrument or EffectControls, so
it will offer functions that must be called by virtuals in its child class
*/
class Lv2ControlBase : public LinkedModelGroups
{
public:
static Plugin::PluginTypes check(const LilvPlugin* m_plugin,
std::vector<PluginIssue> &issues, bool printIssues = false);

const LilvPlugin* getPlugin() const { return m_plugin; }

Lv2Proc *control(std::size_t idx) { return m_procs[idx].get(); }
const Lv2Proc *control(std::size_t idx) const { return m_procs[idx].get(); }

bool hasGui() const { return m_hasGUI; }
void setHasGui(bool val) { m_hasGUI = val; }

protected:
/*
ctor/dtor
*/
//! @param that the class inheriting this class and inheriting Model;
//! this is the same pointer as this, but a different type
//! @param uri the Lv2 URI telling this class what plugin to construct
Lv2ControlBase(class Model *that, const QString& uri);
~Lv2ControlBase() override;
//! Must be checked after ctor or reload
bool isValid() const { return m_valid; }

/*
overrides
*/
LinkedModelGroup* getGroup(std::size_t idx) override;
const LinkedModelGroup* getGroup(std::size_t idx) const override;

/*
utils for the run thread
*/
//! Copy values from all connected models into the respective ports
void copyModelsFromLmms();
//! Copy buffer passed by LMMS into our ports
void copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames);
//! Copy our ports into buffers passed by LMMS
void copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const;
//! Run the Lv2 plugin instance for @param frames frames
void run(fpp_t frames);

/*
load/save, must be called from virtuals
*/
void saveSettings(QDomDocument &doc, QDomElement &that);
void loadSettings(const QDomElement &that);
void loadFile(const QString &file);
//! TODO: not implemented
void reloadPlugin();

/*
more functions that must be called from virtuals
*/
std::size_t controlCount() const;
QString nodeName() const { return "lv2controls"; }

private:
//! Return the DataFile settings type
virtual DataFile::Types settingsType() = 0;
//! Inform the plugin about a file name change
virtual void setNameFromFile(const QString &fname) = 0;

//! Independent processors
//! If this is a mono effect, the vector will have size 2 in order to
//! fulfill LMMS' requirement of having stereo input and output
std::vector<std::unique_ptr<Lv2Proc>> m_procs;

bool m_valid = true;
bool m_hasGUI = false;
unsigned m_channelsPerProc;

const LilvPlugin* m_plugin;
};

#endif // LMMS_HAVE_LV2
#endif // LV2_CONTROL_BASE_H
Loading

0 comments on commit 2a66e83

Please sign in to comment.