From 0e516aa0c1fb40887e423c6713106e814cf19b5e Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 15:39:15 +0100 Subject: [PATCH 001/120] Add PluginIssue class --- include/PluginIssue.h | 66 ++++++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/PluginIssue.cpp | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 include/PluginIssue.h create mode 100644 src/core/PluginIssue.cpp diff --git a/include/PluginIssue.h b/include/PluginIssue.h new file mode 100644 index 00000000000..9207acf0ede --- /dev/null +++ b/include/PluginIssue.h @@ -0,0 +1,66 @@ +/* + * PluginIssue.h - PluginIssue class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 PLUGINISSUE_H +#define PLUGINISSUE_H + +#include +#include + +//! Types of issues that can cause LMMS to not load a plugin +//! LMMS Plugins should use this to indicate errors +enum PluginIssueType +{ + unknownPortFlow, + unknownPortType, + tooManyInputChannels, + tooManyOutputChannels, + noOutputChannel, + portHasNoDef, + portHasNoMin, + portHasNoMax, + featureNotSupported, + badPortType, + noIssue +}; + +//! Issue type bundled with informational string +class PluginIssue +{ + static const char* msgFor(const PluginIssueType& it); + + PluginIssueType m_issueType; + std::string m_info; + +public: + PluginIssue(PluginIssueType it, std::string msg = std::string()) + : m_issueType(it), m_info(msg) + { + } + friend QDebug operator<<(QDebug stream, const PluginIssue& iss); +}; + +QDebug operator<<(QDebug stream, const PluginIssue& iss); + +#endif // PLUGINISSUE_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 85a00780b10..6962b27f7e5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -48,6 +48,7 @@ set(LMMS_SRCS core/Piano.cpp core/PlayHandle.cpp core/Plugin.cpp + core/PluginIssue.cpp core/PluginFactory.cpp core/PresetPreviewPlayHandle.cpp core/ProjectJournal.cpp diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp new file mode 100644 index 00000000000..4a88feb028c --- /dev/null +++ b/src/core/PluginIssue.cpp @@ -0,0 +1,72 @@ +/* + * PluginIssue.h - PluginIssue class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 + +#include "PluginIssue.h" + +const char *PluginIssue::msgFor(const PluginIssueType &it) +{ + switch (it) + { + case unknownPortFlow: + return "unknown port flow for mandatory port"; + case unknownPortType: + return "unknown port type for mandatory port"; + case tooManyInputChannels: + return "too many audio input channels"; + case tooManyOutputChannels: + return "too many audio output channels"; + case noOutputChannel: + return "no audio output channel"; + case portHasNoDef: + return "port is missing default value"; + case portHasNoMin: + return "port is missing min value"; + case portHasNoMax: + return "port is missing max value"; + case featureNotSupported: + return "required feature not supported"; + case badPortType: + return "unsupported port Type"; + case noIssue: + return nullptr; + } + return nullptr; +} + + + + +QDebug operator<<(QDebug stream, const PluginIssue &iss) +{ + stream << PluginIssue::msgFor(iss.m_issueType); + if(iss.m_info.length()) + { + stream.nospace() << ": " << iss.m_info.c_str(); + } + return stream; +} + + From e43d68e1cec886c876c87813c81a6d1a5c1f3f06 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 20:29:48 +0100 Subject: [PATCH 002/120] Add Control classes Add labeled controls for different types with a common base class --- include/Controls.h | 129 +++++++++++++++++++++++++++++++++++ src/gui/CMakeLists.txt | 1 + src/gui/widgets/Controls.cpp | 126 ++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 include/Controls.h create mode 100644 src/gui/widgets/Controls.cpp diff --git a/include/Controls.h b/include/Controls.h new file mode 100644 index 00000000000..e64601bc694 --- /dev/null +++ b/include/Controls.h @@ -0,0 +1,129 @@ +/* + * Controls.h - labeled control widgets + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 CONTROLS_H +#define CONTROLS_H + + +#include "Model.h" + +// headers only required for covariance +#include "AutomatableModel.h" +#include "ComboBoxModel.h" + + +class QString; +class QWidget; +class AutomatableModel; + + +/** + These classes provide + - a control with a text label + - a type safe way to set a model + (justification: setting the wrong typed model to a widget will cause + hard-to-find runtime errors) +*/ +class ControlBase +{ +public: + virtual QWidget* topWidget() = 0; + virtual void setText(const QString& text) = 0; + + virtual void setModel(AutomatableModel* model) = 0; + virtual AutomatableModel* model() = 0; + + virtual ~ControlBase(); +}; + + +class KnobControl : public ControlBase +{ + class Knob* m_knob; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override; + + void setModel(AutomatableModel* model) override; + FloatModel* model() override; + + KnobControl(QWidget* parent = nullptr); + ~KnobControl() override; +}; + + +class ComboControl : public ControlBase +{ + QWidget* m_widget; + class ComboBox* m_combo; + class QLabel* m_label; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override { return m_widget; } + + void setModel(AutomatableModel* model) override; + ComboBoxModel* model() override; + + ComboControl(QWidget* parent = nullptr); + ~ComboControl() override; +}; + + +class LcdControl : public ControlBase +{ + class LcdSpinBox* m_lcd; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override; + + void setModel(AutomatableModel* model) override; + IntModel* model() override; + + LcdControl(int numDigits, QWidget* parent = nullptr); + ~LcdControl() override; +}; + + +class CheckControl : public ControlBase +{ + QWidget* m_widget; + class LedCheckBox* m_checkBox; + QLabel* m_label; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override; + + void setModel(AutomatableModel* model) override; + BoolModel *model() override; + + CheckControl(QWidget* parent = nullptr); + ~CheckControl() override; +}; + + +#endif // CONTROLS_H diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 5b4050bca70..de33c9f3ae5 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -49,6 +49,7 @@ SET(LMMS_SRCS gui/widgets/ComboBox.cpp gui/widgets/ControllerRackView.cpp gui/widgets/ControllerView.cpp + gui/widgets/Controls.cpp gui/widgets/CPULoadWidget.cpp gui/widgets/EffectRackView.cpp gui/widgets/EffectView.cpp diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp new file mode 100644 index 00000000000..cc15b8f6f37 --- /dev/null +++ b/src/gui/widgets/Controls.cpp @@ -0,0 +1,126 @@ +/* + * Controls.cpp - labeled control widgets + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "Controls.h" + +#include +#include +#include + +#include "ComboBox.h" +#include "LcdSpinBox.h" +#include "LedCheckbox.h" +#include "Knob.h" + + + + +ControlBase::~ControlBase() {} + + + + +void KnobControl::setText(const QString &text) { m_knob->setLabel(text); } + +QWidget *KnobControl::topWidget() { return m_knob; } + +void KnobControl::setModel(AutomatableModel *model) +{ + m_knob->setModel(model->dcast(true)); +} + +FloatModel *KnobControl::model() { return m_knob->model(); } + +KnobControl::KnobControl(QWidget *parent) : + m_knob(new Knob(parent)) {} + +KnobControl::~KnobControl() {} + + + + +void ComboControl::setText(const QString &text) { m_label->setText(text); } + +void ComboControl::setModel(AutomatableModel *model) { + m_combo->setModel(model->dcast(true)); } + +ComboBoxModel *ComboControl::model() { return m_combo->model(); } + +ComboControl::ComboControl(QWidget *parent) : + m_widget(new QWidget(parent)), + m_combo(new ComboBox(nullptr)), + m_label(new QLabel(m_widget)) +{ + m_combo->setFixedSize(64, 22); + QVBoxLayout* vbox = new QVBoxLayout(m_widget); + vbox->addWidget(m_combo); + vbox->addWidget(m_label); + m_combo->repaint(); +} + +ComboControl::~ComboControl() {} + + + + +void CheckControl::setText(const QString &text) { m_label->setText(text); } + +QWidget *CheckControl::topWidget() { return m_widget; } + +void CheckControl::setModel(AutomatableModel *model) { + m_checkBox->setModel(model->dcast(true)); } + +BoolModel *CheckControl::model() { return m_checkBox->model(); } + +CheckControl::CheckControl(QWidget *parent) : + m_widget(new QWidget(parent)), + m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::Green)), + m_label(new QLabel(m_widget)) +{ + QVBoxLayout* vbox = new QVBoxLayout(m_widget); + vbox->addWidget(m_checkBox); + vbox->addWidget(m_label); +} + +CheckControl::~CheckControl() {} + + + + +void LcdControl::setText(const QString &text) { m_lcd->setLabel(text); } + +QWidget *LcdControl::topWidget() { return m_lcd; } + +void LcdControl::setModel(AutomatableModel *model) { + m_lcd->setModel(model->dcast(true)); } + +IntModel *LcdControl::model() { return m_lcd->model(); } + +LcdControl::LcdControl(int numDigits, QWidget *parent) : + m_lcd(new LcdSpinBox(numDigits, parent)) +{ +} + +LcdControl::~LcdControl() {} + From f586baca491db11ad83f49cf4a0b24c16cb88dc5 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 20:33:22 +0100 Subject: [PATCH 003/120] Add linked model groups Implement a container for multiple equal groups of linked models and suiting views. Such groups are suited for representing mono effects where each Model occurs twice. A group provides Models for one mono processor and is visually represented with a group box. This concept is common for LADSPA and Lv2, and useful for any mono effect. --- include/LinkedModelGroupViews.h | 90 +++++++++++ include/LinkedModelGroups.h | 157 ++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/LinkedModelGroups.cpp | 174 ++++++++++++++++++++ src/gui/CMakeLists.txt | 1 + src/gui/widgets/LinkedModelGroupViews.cpp | 189 ++++++++++++++++++++++ 6 files changed, 612 insertions(+) create mode 100644 include/LinkedModelGroupViews.h create mode 100644 include/LinkedModelGroups.h create mode 100644 src/core/LinkedModelGroups.cpp create mode 100644 src/gui/widgets/LinkedModelGroupViews.cpp diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h new file mode 100644 index 00000000000..a48c70b2e6c --- /dev/null +++ b/include/LinkedModelGroupViews.h @@ -0,0 +1,90 @@ +/* + * LinkedModelGroupViews.h - views for groups of linkable models + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 LINKEDMODELGROUPVIEWS_H +#define LINKEDMODELGROUPVIEWS_H + + +#include + + +/** + @file LinkedModelGroupViews.h + See Lv2ViewBase.h for example usage +*/ + + +//! View for one processor, LinkedModelGroupsViewBase contains 2 +//! of those for mono plugins +class LinkedModelGroupViewBase : public QGroupBox +{ +public: + //! @param colNum numbers of columns for the controls + //! (link LEDs not counted) + LinkedModelGroupViewBase(QWidget *parent, bool isLinking, + int colNum, int curProc, int nProc, const QString &name = QString()); + ~LinkedModelGroupViewBase(); + + //! Reconnect models if model changed + void modelChanged(class LinkedModelGroup *linkedModelGroup); + +protected: + //! Add a control to this widget + void addControl(class ControlBase *ctrl); + +private: + void makeAllGridCellsEqualSized(); + + int m_colNum; //!< column number in surrounding grid in Lv2ViewBase + bool m_isLinking; + class QGridLayout* m_grid; + QVector m_controls; + QVector m_leds; +}; + + +//! Base class for view for one plugin with linkable models. +//! Provides a global channel link LED. +class LinkedModelGroupsViewBase +{ +protected: + //! @param pluginWidget A child class which inherits QWidget + LinkedModelGroupsViewBase(class LinkedModelGroups *ctrlBase); + ~LinkedModelGroupsViewBase(); + + //! Reconnect models if model changed; to be called by child virtuals + void modelChanged(class LinkedModelGroups* ctrlBase); + + //! Access to the global multi channel link LED + LedCheckBox* globalLinkLed() { return m_multiChannelLink; } + +private: + //! The base class must return the adressed group view + virtual LinkedModelGroupViewBase* getGroupView(std::size_t idx) = 0; + + class LedCheckBox *m_multiChannelLink = nullptr; +}; + + +#endif // LINKEDMODELGROUPVIEWS_H diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h new file mode 100644 index 00000000000..f5b2bcd09d7 --- /dev/null +++ b/include/LinkedModelGroups.h @@ -0,0 +1,157 @@ +/* + * LinkedModelGroups.h - base classes for groups of linkable models + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 LINKEDMODELGROUPS_H +#define LINKEDMODELGROUPS_H + + +#include +#include + +#include "Model.h" + + +/** + @file LinkedModelGroups.h + See Lv2ControlBase.h and Lv2Proc.h for example usage +*/ + + +/** + Base class for a group of linkable models + + See the LinkedModelGroup class for explenations +*/ +class LinkedModelGroup : public Model +{ + Q_OBJECT +signals: + //! Signal emitted after any of the per-control link-enabled models switch + void linkStateChanged(int id, bool value); + +public: + /* + Initialization + */ + //! @param parent model of the LinkedModelGroups class + LinkedModelGroup(Model* parent) : Model(parent) {} + //! After all models have been added, make this processor the one which + //! will contain link models associated with its controls + void makeLinkingProc(); + + /* + Linking + */ + //! Set all per-control link-enabled models to @p state, which will + //! also link or unlink them (via `Lv2ControlBase::linkPort()`) + void linkAllModels(bool state); + //! Link specified port with the associated port of @p other + //! @param id id of the port, conforming to m_models + void linkControls(LinkedModelGroup* other, int id); + //! @see linkControls + void unlinkControls(LinkedModelGroup *other, int id); + //! Return whether this is the first of more than one processors + bool isLinking() { return m_linkEnabled.size(); } + + /* + Models + */ + class BoolModel* linkEnabledModel(std::size_t id) { + return m_linkEnabled[id]; } + std::vector models() { return m_models; } + +protected: + //! Register a further model + void addModel(class AutomatableModel* model); + +private slots: + //! Callback called after any of the per-control link-enabled models switch + void linkStateChangedSlot(); + +private: + //! models for the per-control link-enabled models + std::vector m_linkEnabled; + //! models for the controls; the vector defines indices for the controls + std::vector m_models; +}; + + +/** + Container for multiple equal groups of linked models + + Each group contains the same models and model types. The models are + numbered, and equal numbered models are associated and can be linked. + + A typical application are two mono plugins making a stereo plugin. + + Inheriting classes need to do the following connections, where the slots + must be defined by those classes and call the equal named functions of this + class: + + \code + if(multiChannelLinkModel()) { + connect(multiChannelLinkModel(), SIGNAL(dataChanged()), + this, SLOT(updateLinkStatesFromGlobal())); + connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), + this, SLOT(linkPort(int, bool))); + } + \endcode +*/ +class LinkedModelGroups +{ +public: + virtual ~LinkedModelGroups(); + + //! Return the model for multi channel linking + BoolModel* multiChannelLinkModel() { return m_multiChannelLinkModel.get(); } + //! Create the model for multi channel linking + void createMultiChannelLinkModel(); + + /* + to be called by slots + */ + //! Take a specified port from the first Lv2Proc and link or unlink it + //! from the associated port of every other Lv2Proc + //! @param port number conforming to Lv2Proc::m_modelVector + //! @param state True iff it should be linked + void linkPort(int port, bool state); + //! Callback for the global linking LED + void updateLinkStatesFromGlobal(); + + //! Derived classes must return the group with index @p idx, + //! or nullptr if @p is out of range + virtual LinkedModelGroup* getGroup(std::size_t idx) = 0; + +private: + //! Model for the "global" linking + //! Only allocated if #processors > 1 + std::unique_ptr m_multiChannelLinkModel; + + //! Force updateLinkStatesFromGlobal() to not unlink any ports + //! Implementation detail, see linkPort() implementation + bool m_noLink = false; +}; + + +#endif // LINKEDMODELGROUPS_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b573b93b0bd..00654f92fa5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -30,6 +30,7 @@ set(LMMS_SRCS core/LadspaControl.cpp core/LadspaManager.cpp core/LfoController.cpp + core/LinkedModelGroups.cpp core/LocklessAllocator.cpp core/MemoryHelper.cpp core/MemoryManager.cpp diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp new file mode 100644 index 00000000000..ebee7b1f560 --- /dev/null +++ b/src/core/LinkedModelGroups.cpp @@ -0,0 +1,174 @@ +/* + * LinkedModelGroups.cpp - base classes for groups of linkable models + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "LinkedModelGroups.h" + +#include "AutomatableModel.h" + + + + +/* + LinkedModelGroup +*/ + + + + +void LinkedModelGroup::makeLinkingProc() +{ + for(std::size_t i = 0; i < m_models.size(); ++i) + { + BoolModel* bmo = new BoolModel(true, this, tr("Link channels")); + m_linkEnabled.push_back(bmo); + connect(bmo, SIGNAL(dataChanged()), this, SLOT(linkStateChangedSlot())); + } +} + + + + +void LinkedModelGroup::linkAllModels(bool state) +{ + for(BoolModel* bmo : m_linkEnabled) { bmo->setValue(state); } +} + + + + +void LinkedModelGroup::linkControls(LinkedModelGroup *other, int id) +{ + Q_ASSERT(id >= 0); + std::size_t id2 = static_cast(id); + AutomatableModel::linkModels(m_models[id2], other->m_models[id2]); +} + + + + +void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, int id) +{ + Q_ASSERT(id >= 0); + std::size_t id2 = static_cast(id); + AutomatableModel::unlinkModels( + m_models[id2], other->m_models[id2]); +} + + + + +void LinkedModelGroup::linkStateChangedSlot() +{ + QObject* sender = QObject::sender(); + Q_ASSERT(sender); + BoolModel* bmo = qobject_cast(sender); + Q_ASSERT(bmo); + int modelNo = -1, count = 0; + for(BoolModel* bmo2 : m_linkEnabled) + { + if(bmo2 == bmo) { modelNo = count; } + ++count; + } + Q_ASSERT(modelNo >= 0); + emit linkStateChanged(modelNo, bmo->value()); +} + + + + +void LinkedModelGroup::addModel(AutomatableModel *model) { + m_models.push_back(model); } + + + + +/* + LinkedModelGroups +*/ + + + + +LinkedModelGroups::~LinkedModelGroups() {} + + + + +void LinkedModelGroups::createMultiChannelLinkModel() { + m_multiChannelLinkModel.reset(new BoolModel(true, nullptr)); +} + + + + +void LinkedModelGroups::linkPort(int port, bool state) +{ + LinkedModelGroup* first = getGroup(0); + LinkedModelGroup* cur; + + if(state) + { + for(std::size_t i = 1; (cur=getGroup(i)); ++i) + { + first->linkControls(cur, port); + } + } + else + { + for(std::size_t i = 1; (cur=getGroup(i)); ++i) + { + first->unlinkControls(cur, port); + } + + // m_multiChannelLinkModel.setValue() will call + // updateLinkStatesFromGlobal()... + // m_noLink will make sure that this will not unlink any other ports + m_noLink = true; + m_multiChannelLinkModel->setValue( false ); + } +} + + + + +void LinkedModelGroups::updateLinkStatesFromGlobal() +{ + LinkedModelGroup* first = getGroup(0); + if(m_multiChannelLinkModel->value()) + { + first->linkAllModels(true); + } + else if( !m_noLink ) + { + first->linkAllModels(false); + } + + // if global channel link state has changed, always ignore link + // status of individual ports in the future + m_noLink = false; +} + + + + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index de33c9f3ae5..c6ab433eac4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -68,6 +68,7 @@ SET(LMMS_SRCS gui/widgets/LcdSpinBox.cpp gui/widgets/LcdWidget.cpp gui/widgets/LedCheckbox.cpp + gui/widgets/LinkedModelGroupViews.cpp gui/widgets/MeterDialog.cpp gui/widgets/MidiPortMenu.cpp gui/widgets/NStateButton.cpp diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp new file mode 100644 index 00000000000..3a47ce9cc59 --- /dev/null +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -0,0 +1,189 @@ +/* + * LinkedModelGroupViews.h - views for groups of linkable models + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "LinkedModelGroupViews.h" + +#include + +#include "Controls.h" +#include "LedCheckbox.h" +#include "LinkedModelGroups.h" + + +/* + LinkedModelGroupViewBase +*/ + + +LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, + bool isLinking, int colNum, int curProc, int nProc, const QString& name) : + QGroupBox(parent), + m_colNum(colNum), + m_isLinking(isLinking), + m_grid(new QGridLayout(this)) +{ + QString chanName; + if(name.isNull()) + { + switch(nProc) + { + case 1: break; // don't display any channel name + case 2: + chanName = QObject::tr(curProc ? "Right" : "Left"); + break; + default: + chanName = QObject::tr("Channel ") + QString::number(curProc + 1); + break; + } + } + else { + chanName = name; + } + + if(!chanName.isNull()) { setTitle(chanName); } +} + + + + +LinkedModelGroupViewBase::~LinkedModelGroupViewBase() {} + + + + +void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) +{ + // reconnect models + QVector::Iterator itr = m_controls.begin(); + std::vector models = group->models(); + Q_ASSERT(m_controls.size() == static_cast(models.size())); + + for(AutomatableModel* mdl : models) + { + (*itr++)->setModel(mdl); + } + + std::size_t count = 0; + for (LedCheckBox* led : m_leds) + { + led->setModel(group->linkEnabledModel(count++)); + } +} + + + + +void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) +{ + int colNum2 = m_colNum * (1 + m_isLinking); + int wdgNum = m_controls.size() * (1 + m_isLinking); + if(ctrl) + { + int x = wdgNum%colNum2, y = wdgNum/colNum2; + + // start in row one, add widgets cell by cell + if(m_isLinking) { + LedCheckBox* cb = new LedCheckBox(nullptr); + m_grid->addWidget(cb, y, x); + m_leds.push_back(cb); + } + + m_controls.push_back(ctrl); + m_grid->addWidget(ctrl->topWidget(), y, x + 1, Qt::AlignCenter); + wdgNum += m_isLinking; + ++wdgNum; + } + + // matter of taste (takes a lot of space): + // makeAllGridCellsEqualSized(); +} + + + + +void LinkedModelGroupViewBase::makeAllGridCellsEqualSized() +{ + int rowHeight = 0, colWidth = 0; + for(int row = 0; row < m_grid->rowCount(); ++row) + { + for(int col = 0; col < m_grid->columnCount(); ++col) + { + QLayoutItem* layout; + if((layout = m_grid->itemAtPosition(row, col))) + { + rowHeight = qMax(rowHeight, layout->geometry().height()); + colWidth = qMax(colWidth, layout->geometry().width()); + } + } + } + + for(int row = 0; row < m_grid->rowCount(); ++row) + { + m_grid->setRowMinimumHeight(row, rowHeight); + } + + for(int col = 0; col < m_grid->columnCount(); ++col) + { + m_grid->setColumnMinimumWidth(col, colWidth); + } +} + + +/* + LinkedModelGroupsViewBase +*/ + + +LinkedModelGroupsViewBase::LinkedModelGroupsViewBase( + LinkedModelGroups *ctrlBase) +{ + if(ctrlBase->multiChannelLinkModel()) + { + m_multiChannelLink = new LedCheckBox(QObject::tr("Link Channels"), + nullptr); + } +} + + + + +LinkedModelGroupsViewBase::~LinkedModelGroupsViewBase() {} + + + + +void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) +{ + if(groups->multiChannelLinkModel()) + { + m_multiChannelLink->setModel(groups->multiChannelLinkModel()); + } + + for(std::size_t i = 0; getGroupView(i) && groups->getGroup(i); ++i) + { + getGroupView(i)->modelChanged(groups->getGroup(i)); + } +} + + From 288e83b490ea067bad64ab8c473bab6f895da61a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 17 Mar 2019 11:04:27 +0100 Subject: [PATCH 004/120] Lv2 core implementation For an explenation about the new Lv2 classes, see Lv2Manager.h --- .travis/linux..install.sh | 5 +- CMakeLists.txt | 37 ++ include/Engine.h | 11 + include/Lv2Basics.h | 53 +++ include/Lv2ControlBase.h | 144 ++++++ include/Lv2Manager.h | 158 +++++++ include/Lv2Ports.h | 237 ++++++++++ include/Lv2Proc.h | 155 ++++++ include/Lv2SubPluginFeatures.h | 60 +++ include/Lv2ViewBase.h | 103 ++++ plugins/CMakeLists.txt | 2 + plugins/LadspaEffect/LadspaControls.h | 1 + plugins/Lv2Effect/CMakeLists.txt | 7 + plugins/Lv2Effect/Lv2Effect.cpp | 110 +++++ plugins/Lv2Effect/Lv2Effect.h | 58 +++ plugins/Lv2Effect/Lv2FxControlDialog.cpp | 127 +++++ plugins/Lv2Effect/Lv2FxControlDialog.h | 53 +++ plugins/Lv2Effect/Lv2FxControls.cpp | 111 +++++ plugins/Lv2Effect/Lv2FxControls.h | 68 +++ plugins/Lv2Effect/logo.png | Bin 0 -> 967 bytes plugins/Lv2Instrument/CMakeLists.txt | 7 + plugins/Lv2Instrument/Lv2Instrument.cpp | 459 ++++++++++++++++++ plugins/Lv2Instrument/Lv2Instrument.h | 130 +++++ plugins/Lv2Instrument/logo.png | Bin 0 -> 967 bytes src/CMakeLists.txt | 15 + src/core/CMakeLists.txt | 7 + src/core/DataFile.cpp | 3 + src/core/Engine.cpp | 11 + src/core/lv2/Lv2ControlBase.cpp | 327 +++++++++++++ src/core/lv2/Lv2Manager.cpp | 128 +++++ src/core/lv2/Lv2Ports.cpp | 259 ++++++++++ src/core/lv2/Lv2Proc.cpp | 578 +++++++++++++++++++++++ src/core/lv2/Lv2SubPluginFeatures.cpp | 199 ++++++++ src/gui/CMakeLists.txt | 1 + src/gui/FileBrowser.cpp | 11 +- src/gui/Lv2ViewBase.cpp | 249 ++++++++++ src/gui/MainWindow.cpp | 2 +- src/lmmsconfig.h.in | 1 + 38 files changed, 3884 insertions(+), 3 deletions(-) create mode 100644 include/Lv2Basics.h create mode 100644 include/Lv2ControlBase.h create mode 100644 include/Lv2Manager.h create mode 100644 include/Lv2Ports.h create mode 100644 include/Lv2Proc.h create mode 100644 include/Lv2SubPluginFeatures.h create mode 100644 include/Lv2ViewBase.h create mode 100644 plugins/Lv2Effect/CMakeLists.txt create mode 100644 plugins/Lv2Effect/Lv2Effect.cpp create mode 100644 plugins/Lv2Effect/Lv2Effect.h create mode 100644 plugins/Lv2Effect/Lv2FxControlDialog.cpp create mode 100644 plugins/Lv2Effect/Lv2FxControlDialog.h create mode 100644 plugins/Lv2Effect/Lv2FxControls.cpp create mode 100644 plugins/Lv2Effect/Lv2FxControls.h create mode 100644 plugins/Lv2Effect/logo.png create mode 100644 plugins/Lv2Instrument/CMakeLists.txt create mode 100644 plugins/Lv2Instrument/Lv2Instrument.cpp create mode 100644 plugins/Lv2Instrument/Lv2Instrument.h create mode 100644 plugins/Lv2Instrument/logo.png create mode 100644 src/core/lv2/Lv2ControlBase.cpp create mode 100644 src/core/lv2/Lv2Manager.cpp create mode 100644 src/core/lv2/Lv2Ports.cpp create mode 100644 src/core/lv2/Lv2Proc.cpp create mode 100644 src/core/lv2/Lv2SubPluginFeatures.cpp create mode 100644 src/gui/Lv2ViewBase.cpp diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index ab4fba26395..e7751cbed1e 100755 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 88aefadae8d..309fcde5390 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,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 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) @@ -176,6 +178,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") @@ -674,6 +709,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" diff --git a/include/Engine.h b/include/Engine.h index 18960ec8f4c..c76ce4ff049 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -30,6 +30,7 @@ #include +#include "lmmsconfig.h" #include "lmms_export.h" class BBTrackContainer; @@ -86,6 +87,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; @@ -139,6 +147,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; diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h new file mode 100644 index 00000000000..e6ff63d6ed9 --- /dev/null +++ b/include/Lv2Basics.h @@ -0,0 +1,53 @@ +/* + * Lv2Basics.h - basic Lv2 utils + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 + + +//! a simple RAII class for lilv nodes that shall be freed +struct AutoLilvNode +{ + LilvNode* n; + AutoLilvNode(LilvNode* n) : n(n) {} + AutoLilvNode(const AutoLilvNode& other) = delete; + AutoLilvNode(AutoLilvNode&& other) { + n = other.n; + other.n = nullptr; + } + ~AutoLilvNode() { if(n) lilv_node_free(n); } + const LilvNode* get() const { return n; } +}; + + +#endif // LMMS_HAVE_LV2 +#endif // LV2BASICS_H diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h new file mode 100644 index 00000000000..6d357a4f063 --- /dev/null +++ b/include/Lv2ControlBase.h @@ -0,0 +1,144 @@ +/* + * Lv2ControlBase.h - Lv2 control base class + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 + +// general LMMS includes +#include "DataFile.h" +#include "lmms_basics.h" +#include "LinkedModelGroups.h" +#include "Lv2Basics.h" +#include "Model.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 &issues, bool printIssues = false); + + const LilvPlugin* getPlugin() const { return m_plugin; } + + bool hasGui() const { return m_hasGUI; } + void setHasGui(bool val) { m_hasGUI = val; } + std::vector& controls() { return m_procs; } + +protected: + /* + ctor/dtor + */ + //! @param parent the class inheriting this class + //! @param uri the Lv2 URI telling this class what plugin to construct + Lv2ControlBase(Model *that, const QString& uri); + virtual ~Lv2ControlBase() override; + //! Must be checked after ctor or reload + bool isValid() const { return m_valid; } + + /* + overrides + */ + LinkedModelGroup* getGroup(std::size_t idx) 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(unsigned 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 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 diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h new file mode 100644 index 00000000000..cf5aad572de --- /dev/null +++ b/include/Lv2Manager.h @@ -0,0 +1,158 @@ +/* + * Lv2Manager.h - Implementation of Lv2Manager class + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 LV2MANAGER_H +#define LV2MANAGER_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include + +#include "Lv2Basics.h" +#include "Plugin.h" + + +/* + all Lv2 classes in relation (use our "4 spaces per tab rule" to view): + + explanation: + "x = {y z}" means class "x" consists of classes "y" and "z" + (and probably other classes not mentioned) + "x = {y*}" me}ns class "x" references/uses class "y" + + core: + Lv2Proc = {LilvInstance} + Lv2ControlBase = {Lv2Proc, Lv2Proc... (2 for mono, 1 for stereo)} + Lv2Manager = {LilvPlugin*, LilvPlugin* ...} + (creates Lv2ControlBase, Lv2ControlBase...) + + Lv2FxControls = {LilvControlBase} + Lv2Effect = {Effect + Lv2FxControls} + (takes Lv2SubPluginFeatures in ctor) + Lv2Instrument = {Instrument + LilvControlBase} + (takes Lv2SubPluginFeatures in ctor) + + gui: + Lv2ViewProc = {Lv2Proc*} + Lv2ViewBase = {Lv2ViewProc, Lv2ViewProc... + (2 for mono, 1 for stereo)} + Lv2FxControlDialog = {EffectControlDialog + Lv2ViewBase} + Lv2InsView = {InstrumentView + Lv2ViewBase} + + Lv2SubPluginFeatures: + Lv2SubPluginFeatures = {Lv2Manager*} + Lv2Effect::Descriptor = {Lv2SubPluginFeatures} + Lv2Instrument::Descriptor = {Lv2SubPluginFeatures} +*/ + + +//! Class to keep track of all LV2 plugins +class Lv2Manager +{ + LilvWorld* m_world; + +public: + void initPlugins(); + + Lv2Manager(); + ~Lv2Manager(); + + + AutoLilvNode uri(const char* uriStr) + { + return AutoLilvNode(lilv_new_uri(m_world, uriStr)); + } + + //! Class representing info for one plugin + struct Lv2Info + { + public: + //! use only for std::map internals + Lv2Info() : m_plugin(nullptr) {} + //! ctor used inside Lv2Manager + Lv2Info(const LilvPlugin* plug, Plugin::PluginTypes type, bool valid) : + m_plugin(plug), m_type(type), m_valid(valid) {} + Lv2Info(const Lv2Info &) = delete; + Lv2Info(Lv2Info&& other) : + m_plugin(other.m_plugin), + m_type(std::move(other.m_type)), + m_valid(std::move(other.m_valid)) + { + } + Lv2Info& operator=(Lv2Info&& other) + { + m_plugin = other.m_plugin; + m_type = std::move(other.m_type); + m_valid = std::move(other.m_valid); + return *this; + } + bool isValid() const { return m_valid; } + Plugin::PluginTypes type() const { return m_type; } + const LilvPlugin* plugin() const { return m_plugin; } + private: + const LilvPlugin* m_plugin; + Plugin::PluginTypes m_type; + bool m_valid = false; + }; + + //! Return a descriptor with @p uniqueName or nullptr if none exists + //! @param uniqueName The lv2::unique_name of the plugin + const LilvPlugin *getPlugin(const std::string &uri); + const LilvPlugin *getPlugin(const QString uri); + + class Iterator + { + std::map::iterator m_itr; + public: + bool operator!=(const Iterator &other) + { + return m_itr != other.m_itr; + } + Iterator &operator++() + { + ++m_itr; + return *this; + } + std::pair &operator*() + { + return *m_itr; + } + Iterator(std::map::iterator itr) : + m_itr(itr) {} + }; + + Iterator begin() { return Iterator(m_lv2InfoMap.begin()); } + Iterator end() { return Iterator(m_lv2InfoMap.end()); } + +private: + std::map m_lv2InfoMap; + bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); +}; + +#endif // LMMS_HAVE_LV2 + +#endif // LV2MANAGER_H diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h new file mode 100644 index 00000000000..5c1341b00ff --- /dev/null +++ b/include/Lv2Ports.h @@ -0,0 +1,237 @@ +/* + * Lv2Ports.h - Lv2 port definitions + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 LV2PORTS_H +#define LV2PORTS_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include + +#include "lmms_basics.h" +#include "PluginIssue.h" + +struct ConnectPorts; + +namespace Lv2Ports { + +/* + port structs +*/ +enum class Flow { + Unknown, + Input, + Output +}; + +enum class Type { + Unknown, + Control, + Audio, + Event, //!< TODO: unused, describe + Cv //!< TODO: unused, describe +}; + +//! Port visualization +//! @note All Lv2 audio ports are float, this is only the visualisation +enum class Vis { + None, + Integer, + Enumeration, + Toggled +}; + +const char* toStr(Lv2Ports::Flow pf); +const char* toStr(Lv2Ports::Type pt); +const char* toStr(Lv2Ports::Vis pv); + + +struct ConstVisitor +{ +#define CAN_VISIT(clss) \ +virtual void visit(const struct clss& ) {} + + CAN_VISIT(ControlPortBase) + CAN_VISIT(Control) + CAN_VISIT(Audio) + CAN_VISIT(Cv) + CAN_VISIT(Unknown) + virtual ~ConstVisitor(); +#undef CAN_VISIT +}; + +struct Visitor +{ +#define CAN_VISIT(clss) \ +virtual void visit(clss& ) {} + + CAN_VISIT(ControlPortBase) + CAN_VISIT(Control) + CAN_VISIT(Audio) + CAN_VISIT(Cv) + CAN_VISIT(Unknown) + virtual ~Visitor(); +#undef CAN_VISIT +}; + +struct Meta +{ + Type m_type = Type::Unknown; + Flow m_flow = Flow::Unknown; + Vis m_vis = Vis::None; + + float m_def = .0f, m_min = .0f, m_max = .0f; + bool m_optional = false; + bool m_used = true; + + std::vector get(const LilvPlugin* plugin, unsigned portNum); +}; + +struct PortBase : public Meta +{ + const LilvPort* m_port = nullptr; + const LilvPlugin* m_plugin = nullptr; + + virtual void accept(Visitor& v) = 0; + virtual void accept(ConstVisitor& v) const = 0; + + QString name() const; + + virtual ~PortBase(); +}; + +#define IS_PORT_TYPE \ +void accept(Visitor& v) override { v.visit(*this); } \ +void accept(ConstVisitor& v) const override { v.visit(*this); } + +struct ControlPortBase : public PortBase +{ + IS_PORT_TYPE + + //! LMMS models + //! Always up-to-date, except during runs + std::unique_ptr m_connectedModel; + + //! Enumerate float values + //! lv2 defines scale points as + //! "single float Points (for control inputs)" + std::vector m_scalePointMap; +}; + +struct Control : public ControlPortBase +{ + IS_PORT_TYPE + + //! Data location which Lv2 plugins see + //! Model values are being copied here every run + //! Between runs, this data is not up-to-date + float m_val; +}; + +struct Cv : public ControlPortBase +{ + IS_PORT_TYPE + + //! Data location which Lv2 plugins see + //! Model values are being copied here every run + //! Between runs, this data is not up-to-date + std::vector m_buffer; +}; + +struct Audio : public PortBase +{ + IS_PORT_TYPE + + Audio(std::size_t bufferSize, bool isSidechain); + + //! Copy buffer passed by LMMS into our ports + void copyBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames); + //! Add buffer passed by LMMS into our ports, and halve the result + void addBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames); + //! Copy our ports into buffers passed by LMMS + void copyBuffersToCore(sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) const; + + bool isSideChain() const { return m_sidechain; } + std::size_t bufferSize() const { return m_buffer.size(); } + +private: + std::vector m_buffer; + bool m_sidechain; + + // the only case when data of m_buffer may be referenced: + friend struct ::ConnectPorts; +}; + +struct Unknown : public PortBase +{ + IS_PORT_TYPE +}; +#undef IS_PORT_TYPE + +/* + port casts +*/ +template +struct DCastVisitor : public Visitor +{ + Target* m_result = nullptr; + void visit(Target& tar) { m_result = &tar; } +}; + +template +struct ConstDCastVisitor : public ConstVisitor +{ + const Target* m_result = nullptr; + void visit(const Target& tar) { m_result = &tar; } +}; + +//! If you don't want to use a whole visitor, you can use dcast +template +Target* dcast(PortBase* base) +{ + DCastVisitor vis; + base->accept(vis); + return vis.m_result; +} + +//! const overload +template +const Target* dcast(const PortBase* base) +{ + ConstDCastVisitor vis; + base->accept(vis); + return vis.m_result; +} + +} // namespace Lv2Ports + +#endif // LMMS_HAVE_LV2 +#endif // LV2PORTS_H diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h new file mode 100644 index 00000000000..0b65a27d369 --- /dev/null +++ b/include/Lv2Proc.h @@ -0,0 +1,155 @@ +/* + * Lv2Proc.h - Lv2 processor class + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 LV2PROC_H +#define LV2PROC_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include + +#include "Lv2Basics.h" +#include "LinkedModelGroups.h" +#include "Plugin.h" +#include "PluginIssue.h" + + +// forward declare port structs/enums +namespace Lv2Ports +{ + struct Audio; + struct PortBase; + + enum class Type; + enum class Flow; + enum class Vis; +} + + +//! Class representing one Lv2 processor, i.e. one Lv2 handle +//! For Mono effects, 1 Lv2ControlBase references 2 Lv2Proc +class Lv2Proc : public LinkedModelGroup +{ +public: + static Plugin::PluginTypes check(const LilvPlugin* plugin, + std::vector &issues, bool printIssues = false); + + /* + ctor/dtor + */ + Lv2Proc(const LilvPlugin* plugin, Model *parent); + virtual ~Lv2Proc(); + //! Must be checked after ctor or reload + bool isValid() const { return m_valid; } + + /* + port access + */ + struct StereoPortRef + { + //! mono port or left port in case of stereo + Lv2Ports::Audio* m_left = nullptr; + //! unused, or right port in case of stereo + Lv2Ports::Audio* m_right = nullptr; + }; + + StereoPortRef& inPorts() { return m_inPorts; } + const StereoPortRef& inPorts() const { return m_inPorts; } + StereoPortRef& outPorts() { return m_outPorts; } + const StereoPortRef& outPorts() const { return m_outPorts; } + std::vector& getPorts() { return m_ports; } + const std::vector& getPorts() const { return m_ports; } + + //! Debug function to print ports to stdout + void dumpPorts(); + + /* + utils for the run thread + */ + //! Copy values from all connected models into the respective ports + void copyModelsFromCore(); + //! Copy buffer passed by the core into our ports + //! @param offset Offset in each frame of @p buf to take + //! @param num Number of buffers provided in @param buf + void copyBuffersFromCore(const sampleFrame *buf, + unsigned offset, unsigned num, fpp_t frames); + //! Copy our ports into buffers passed by the core + //! @param offset Offset in each frame of @p buf to fill + //! @param num Number of buffers in @param buf we must fill + void copyBuffersToCore(sampleFrame *buf, unsigned offset, unsigned num, + fpp_t frames) const; + //! Run the Lv2 plugin instance for @param frames frames + void run(unsigned frames); + + /* + misc + */ + class AutomatableModel *modelAtPort(const QString &uri); // unused currently + std::size_t controlCount() const { return m_controlCount; } + +protected: + /* + load and save + */ + //! Create ports and instance, connect ports, activate plugin + void initPlugin(); + //! Deactivate instance + void shutdownPlugin(); + +private: + bool m_valid = true; + + const LilvPlugin* m_plugin; + LilvInstance* m_instance; + + std::vector m_ports; + StereoPortRef m_inPorts, m_outPorts; + std::size_t m_controlCount = 0; + + //! models for the controls, sorted by port symbols + std::map m_connectedModels; + + //! load a file in the plugin, but don't do anything in LMMS + void loadFileInternal(const QString &file); + //! allocate m_ports, fill all with metadata, and assign meaning of ports + void createPorts(); + //! fill m_ports[portNum] with metadata + void createPort(unsigned portNum); + //! connect m_ports[portNum] with Lv2 + void connectPort(unsigned num); + //! clean up all ports + void destroyPorts(); + + void dumpPort(std::size_t num); + + static bool portIsSideChain(const LilvPlugin* plugin, const LilvPort *port); + static AutoLilvNode uri(const char* uriStr); +}; + +#endif // LMMS_HAVE_LV2 +#endif // LV2PROC_H diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h new file mode 100644 index 00000000000..30d6dad8a35 --- /dev/null +++ b/include/Lv2SubPluginFeatures.h @@ -0,0 +1,60 @@ +/* + * Lv2SubPluginFeatures.h - derivation from + * Plugin::Descriptor::SubPluginFeatures for + * hosting LV2 plugins + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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_SUBPLUGIN_FEATURES_H +#define LV2_SUBPLUGIN_FEATURES_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "Plugin.h" + +class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures +{ +private: + static const LilvPlugin *getPlugin(const Key &k); + +public: + Lv2SubPluginFeatures(Plugin::PluginTypes _type); + + virtual void fillDescriptionWidget( + QWidget *_parent, const Key *k) const override; + + QString additionalFileExtensions(const Key &k) const override; + QString displayName(const Key &k) const override; + QString description(const Key &k) const override; + const PixmapLoader *logo(const Key &k) const override; + + void listSubPluginKeys( + const Plugin::Descriptor *_desc, KeyList &_kl) const override; +}; + +#endif // LMMS_HAVE_LV2 + +#endif diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h new file mode 100644 index 00000000000..87dc04aa99c --- /dev/null +++ b/include/Lv2ViewBase.h @@ -0,0 +1,103 @@ +/* + * Lv2ViewBase.h - base class for Lv2 plugin views + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 LV2VIEWBASE_H +#define LV2VIEWBASE_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + + +#include +#include + +#include "LinkedModelGroupViews.h" +#include "Lv2Basics.h" + +class Lv2Proc; +class Lv2ControlBase; + + +//! View for one processor, Lv2ViewBase contains 2 of those for mono plugins +class Lv2ViewProc : public LinkedModelGroupViewBase +{ +public: + //! @param colNum numbers of columns for the controls + //! (link LEDs not counted) + Lv2ViewProc(QWidget *parent, Lv2Proc *ctrlBase, + int colNum, int curProc, int nProc, const QString &name = QString()); + ~Lv2ViewProc(); + +private: + static AutoLilvNode uri(const char *uriStr); +}; + + +//! Base class for view for one Lv2 plugin +class Lv2ViewBase : public LinkedModelGroupsViewBase +{ +protected: + //! @param pluginWidget A child class which inherits QWidget + Lv2ViewBase(class QWidget *pluginWidget, Lv2ControlBase *ctrlBase); + ~Lv2ViewBase(); + + // these widgets must be connected by child widgets + class QPushButton *m_reloadPluginButton = nullptr; + class QPushButton *m_toggleUIButton = nullptr; + class QPushButton *m_helpButton = nullptr; + + // to be called by child slots + void toggleHelp(bool visible); + + // to be called by child virtuals + //! Reconnect models if model changed + void modelChanged(Lv2ControlBase* ctrlBase); + +private: + enum Rows + { + ButtonRow, + ProcRow, + LinkChannelsRow + }; + + static AutoLilvNode uri(const char *uriStr); + LinkedModelGroupViewBase* getGroupView(std::size_t idx) override { + return static_cast(idx) < m_procViews.size() + ? m_procViews[static_cast(idx)] + : nullptr; + } + + QVector m_procViews; + + //! Numbers of controls per row; must be multiple of 2 for mono effects + const int m_colNum = 6; + class QMdiSubWindow* m_helpWindow = nullptr; + class LedCheckBox *m_multiChannelLink; +}; + + +#endif // LMMS_HAVE_LV2 +#endif // LV2VIEWBASE_H diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b4a1081f68f..389ab281ef8 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -56,6 +56,8 @@ IF("${PLUGIN_LIST}" STREQUAL "") HydrogenImport ladspa_browser LadspaEffect + Lv2Effect + Lv2Instrument lb302 MidiImport MidiExport diff --git a/plugins/LadspaEffect/LadspaControls.h b/plugins/LadspaEffect/LadspaControls.h index ccb96dd7484..013b914d45d 100644 --- a/plugins/LadspaEffect/LadspaControls.h +++ b/plugins/LadspaEffect/LadspaControls.h @@ -71,6 +71,7 @@ protected slots: ch_cnt_t m_controlCount; bool m_noLink; BoolModel m_stereoLinkModel; + //! control vector for each processor QVector m_controls; diff --git a/plugins/Lv2Effect/CMakeLists.txt b/plugins/Lv2Effect/CMakeLists.txt new file mode 100644 index 00000000000..7f146a0a5b6 --- /dev/null +++ b/plugins/Lv2Effect/CMakeLists.txt @@ -0,0 +1,7 @@ +IF(LMMS_HAVE_LV2) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + INCLUDE(BuildPlugin) + BUILD_PLUGIN(lv2effect Lv2Effect.cpp Lv2FxControls.cpp Lv2FxControlDialog.cpp Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h MOCFILES Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h EMBEDDED_RESOURCES logo.png) +ENDIF(LMMS_HAVE_LV2) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp new file mode 100644 index 00000000000..33c8a19a1ae --- /dev/null +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -0,0 +1,110 @@ +/* + * Lv2Effect.cpp - implementation of LV2 effect + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2Effect.h" + +#include +#include + +#include "AutomatableModel.h" +#include "Lv2FxControlDialog.h" +#include "Lv2SubPluginFeatures.h" +#include "embed.h" +#include "plugin_export.h" + + + + +Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = +{ + STRINGIFY(PLUGIN_NAME), + "LV2", + QT_TRANSLATE_NOOP("Lv2Effect", + "plugin for using arbitrary LV2-effects inside LMMS."), + "Johannes Lorenz ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader("logo"), + nullptr, + new Lv2SubPluginFeatures(Plugin::Effect) +}; + + + + +Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *key) : + Effect(&lv2effect_plugin_descriptor, parent, key), + m_controls(this, key->attributes["uri"]) +{ +} + + + + +Lv2Effect::~Lv2Effect() +{ +} + + + + +bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +{ + if(!isEnabled() || !isRunning()) + { + return false; + } + + Lv2FxControls& ctrl = m_controls; + + ctrl.copyBuffersFromLmms(buf, frames); + + m_controls.copyModelsFromLmms(); + +// m_pluginMutex.lock(); + ctrl.run(static_cast(frames)); +// m_pluginMutex.unlock(); + + ctrl.copyBuffersToLmms(buf, frames); + + return isRunning(); +} + + + + +extern "C" +{ + +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) +{ + using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; + Lv2Effect* eff = new Lv2Effect(_parent, static_cast(_data)); + if(!eff->isValid()) + eff = nullptr; + return eff; +} + +} diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h new file mode 100644 index 00000000000..787fd5eff3d --- /dev/null +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -0,0 +1,58 @@ +/* + * Lv2Effect.h - implementation of LV2 effect + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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_EFFECT_H +#define LV2_EFFECT_H + +#include + +#include "Effect.h" +#include "Lv2FxControls.h" + +class Lv2Effect : public Effect +{ + Q_OBJECT + +public: + /* + initialization + */ + Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key* _key); + ~Lv2Effect() override; + //! Must be checked after ctor or reload + bool isValid() const { return m_controls.isValid(); } + + bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + EffectControls* controls() override { return &m_controls; } + + Lv2FxControls* lv2Controls() { return &m_controls; } + const Lv2FxControls* lv2Controls() const { return &m_controls; } + +private: + Lv2FxControls m_controls; + + friend class Lv2FxControls; +}; + +#endif // LMMS_HAVE_LV2 diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp new file mode 100644 index 00000000000..04c7f79aff5 --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -0,0 +1,127 @@ +/* + * Lv2ControlDialog.cpp - control dialog for amplifier effect + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2FxControlDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Knob.h" +#include "LcdSpinBox.h" +#include "LedCheckbox.h" +#include "Lv2Effect.h" +#include "Lv2FxControls.h" +#include "embed.h" +#include "gui_templates.h" + + +Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : + EffectControlDialog(controls), + Lv2ViewBase(this, controls) +{ + if(m_reloadPluginButton) { + connect(m_reloadPluginButton, SIGNAL(toggled(bool)), + this, SLOT(reloadPlugin())); + } + if(m_toggleUIButton) { + connect(m_toggleUIButton, SIGNAL(toggled(bool)), + this, SLOT(toggleUI())); + } + if(m_helpButton) { + connect(m_helpButton, SIGNAL(toggled(bool)), + this, SLOT(toggleHelp(bool))); + } + // for Effects, modelChanged only goes to the top EffectView + // we need to call it manually + modelChanged(); +} + + + + +/* +// TODO: common UI class..., as this must be usable for instruments, too +Lv2ControlDialog::~Lv2ControlDialog() +{ + Lv2Effect *model = castModel(); + + if (model && lv2Controls()->m_lv2Descriptor->ui_ext() && +lv2Controls()->m_hasGUI) + { + qDebug() << "shutting down UI..."; + model->m_plugin->ui_ext_show(false); + } +} +*/ + + + + +void Lv2FxControlDialog::reloadPlugin() { lv2Controls()->reloadPlugin(); } + + + + +void Lv2FxControlDialog::toggleUI() +{ +#if 0 + Lv2Effect *model = castModel(); + if (model->m_lv2Descriptor->ui_ext() && + model->m_hasGUI != m_toggleUIButton->isChecked()) + { + model->m_hasGUI = m_toggleUIButton->isChecked(); + model->m_plugin->ui_ext_show(model->m_hasGUI); + ControllerConnection::finalizeConnections(); + } +#endif +} + +void Lv2FxControlDialog::toggleHelp(bool visible) +{ + Lv2ViewBase::toggleHelp(visible); +} + + + + +Lv2FxControls *Lv2FxControlDialog::lv2Controls() +{ + return static_cast(m_effectControls); +} + + + + +void Lv2FxControlDialog::modelChanged() +{ + Lv2ViewBase::modelChanged(lv2Controls()); +} + + diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.h b/plugins/Lv2Effect/Lv2FxControlDialog.h new file mode 100644 index 00000000000..d7bace0db19 --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControlDialog.h @@ -0,0 +1,53 @@ +/* + * Lv2ControlDialog.h - control dialog for amplifier effect + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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_FX_CONTROL_DIALOG_H +#define LV2_FX_CONTROL_DIALOG_H + +#include "EffectControlDialog.h" +#include "Lv2ViewBase.h" + +class Lv2FxControls; + + +class Lv2FxControlDialog : public EffectControlDialog, public Lv2ViewBase +{ + Q_OBJECT + +public: + Lv2FxControlDialog(Lv2FxControls *controls); + virtual ~Lv2FxControlDialog() override {} + +private slots: + void reloadPlugin(); + void toggleUI(); + void toggleHelp(bool visible); + +private: + Lv2FxControls *lv2Controls(); + void modelChanged() override; +}; + + +#endif diff --git a/plugins/Lv2Effect/Lv2FxControls.cpp b/plugins/Lv2Effect/Lv2FxControls.cpp new file mode 100644 index 00000000000..ef6f710c8ee --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControls.cpp @@ -0,0 +1,111 @@ +/* + * Lv2Controls.cpp - controls for amplifier effect + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2FxControls.h" + +#include + +#include "Engine.h" +#include "Lv2Effect.h" +#include "Lv2FxControlDialog.h" +#include "Lv2Proc.h" + + + + +Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) : + EffectControls(effect), + Lv2ControlBase(this, uri), + m_effect(effect) +{ + if (isValid()) + { + connect(Engine::mixer(), SIGNAL(sampleRateChanged()), this, + SLOT(reloadPlugin())); + if(multiChannelLinkModel()) { + connect(multiChannelLinkModel(), SIGNAL(dataChanged()), + this, SLOT(updateLinkStatesFromGlobal())); + connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), + this, SLOT(linkPort(int, bool))); + } + } +} + + + + +void Lv2FxControls::saveSettings(QDomDocument &doc, QDomElement &that) +{ + Lv2ControlBase::saveSettings(doc, that); +} + + + + +void Lv2FxControls::loadSettings(const QDomElement &that) +{ + Lv2ControlBase::loadSettings(that); +} + + + + +int Lv2FxControls::controlCount() +{ + return static_cast(Lv2ControlBase::controlCount()); +} + + + + +EffectControlDialog *Lv2FxControls::createView() +{ + return new Lv2FxControlDialog(this); +} + + + + +void Lv2FxControls::changeControl() // TODO: what is that? +{ + // engine::getSong()->setModified(); +} + + + + +DataFile::Types Lv2FxControls::settingsType() +{ + return DataFile::EffectSettings; +} + + + + +void Lv2FxControls::setNameFromFile(const QString &name) +{ + effect()->setDisplayName(name); +} + + diff --git a/plugins/Lv2Effect/Lv2FxControls.h b/plugins/Lv2Effect/Lv2FxControls.h new file mode 100644 index 00000000000..fd0adda491e --- /dev/null +++ b/plugins/Lv2Effect/Lv2FxControls.h @@ -0,0 +1,68 @@ +/* + * Lv2Controls.h - controls for bassboosterx -effect + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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_FX_CONTROLS_H +#define LV2_FX_CONTROLS_H + +#include "EffectControls.h" +#include "Lv2ControlBase.h" + +class Lv2Effect; + + +class Lv2FxControls : public EffectControls, public Lv2ControlBase +{ + Q_OBJECT +public: + Lv2FxControls(Lv2Effect *effect, const QString &uri); + ~Lv2FxControls() override {} + + void saveSettings(QDomDocument &_doc, QDomElement &_parent) override; + void loadSettings(const QDomElement &that) override; + inline QString nodeName() const override + { + return Lv2ControlBase::nodeName(); + } + + int controlCount() override; + EffectControlDialog *createView() override; + +private slots: + void changeControl(); + void reloadPlugin() { Lv2ControlBase::reloadPlugin(); } + void updateLinkStatesFromGlobal() { + Lv2ControlBase::updateLinkStatesFromGlobal(); } + void linkPort(int id, bool state) { Lv2ControlBase::linkPort(id, state); } + + +private: + DataFile::Types settingsType() override; + void setNameFromFile(const QString &name) override; + + Lv2Effect *m_effect; + friend class Lv2FxControlDialog; + friend class Lv2Effect; +}; + +#endif diff --git a/plugins/Lv2Effect/logo.png b/plugins/Lv2Effect/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c423ccea45839a6296fa79d972484b2fad7c0f51 GIT binary patch literal 967 zcmV;&133JNP)aE)q(*?>IU8e zs(@dBpMl9`4&Wi631|fV0QQ*KXGH$|MoANrew5TwYE+Tb>b^}#YK&F*T+$6mb*0L8 zN$Vutlyo6ftd=w->2Rq}T1iLT*VO>!E^qe9`d!4J-gV~h-^}c0Lcbn(G+~lAvynMC zQV&!lM1Pvu6-kxJ_9OV#{b&Y;rW@?N>Jy`KtCy|CdK($N%sP~K1nrh zKP>58NsmWt|L5ysERh32xg6M$VzUFtMcR4M?Hho-z$Hmf&eqU<2ViESz*m8IXNuWN zL3zk&HwqjCzI6Fxz_BF_pui|>lGIdyK21PdAnx~{aoWrdn^_0&q3b$}5x~blevYFM z%JXg?;s<}{mOKD6y8#Ra;$BH*!QU?fdV_jr%xpZUR|V|x;@x75K#WG~fM)_(r^{#1 zC*k}Pzp8iZuc;8a)E^`1kX{j-8~>0pKnFp74xwubKUo*5RTCU}i&} zyT@n~(r3iXzLvBNcstM?k~HSem12$n!23bDOHwPay+FO!1NjPIZSWLC`2rkllhji{ z_mex;mpCltfR7@54S3FU8~AjV{-)-VqRuCon+<2(?u(Rq7w;pTUi4%xiS3eVBa=C8 zvM(5m9YD@A%~QUb#PD~4)L}s=Pr0QKaBH4zcAMEzNuL1S5&dAwfn{DdDboR5_jg^e zEanp%%Y2vAT}yPToK8iqB`I{-hqmJZxXaq=^t>F-?tVWkx0mpNevA8fGN!EZo$rpM zS0z=KkmJ>o_PK9ko^D5P=IVhHz|$`JJ#tVfHY$Kx|Go&k?8n&Xs)M8)@GP(yc#6`v p8yyC|0nVA(6d5vP$dDm}<6kPu9i$iGsP6y(002ovPDHLkV1fcwztR8z literal 0 HcmV?d00001 diff --git a/plugins/Lv2Instrument/CMakeLists.txt b/plugins/Lv2Instrument/CMakeLists.txt new file mode 100644 index 00000000000..290bd84e82e --- /dev/null +++ b/plugins/Lv2Instrument/CMakeLists.txt @@ -0,0 +1,7 @@ +IF(LMMS_HAVE_LV2) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + INCLUDE(BuildPlugin) + BUILD_PLUGIN(lv2instrument Lv2Instrument.cpp Lv2Instrument.h MOCFILES Lv2Instrument.h EMBEDDED_RESOURCES logo.png) +ENDIF(LMMS_HAVE_LV2) diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp new file mode 100644 index 00000000000..53e88362dfa --- /dev/null +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -0,0 +1,459 @@ +/* + * Lv2Instrument.cpp - implementation of LV2 instrument + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2Instrument.h" + +#include +#include +#include +#include +#include + +#include "AutomatableModel.h" +#include "ControllerConnection.h" +#include "InstrumentPlayHandle.h" +#include "InstrumentTrack.h" +#include "Mixer.h" +#include "LedCheckbox.h" +#include "Lv2Proc.h" +#include "Lv2SubPluginFeatures.h" +#include "StringPairDrag.h" // DnD +#include "gui_templates.h" +#include "embed.h" +#include "plugin_export.h" + + + + +Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = +{ + STRINGIFY(PLUGIN_NAME), + "LV2", + QT_TRANSLATE_NOOP("Lv2Instrument", + "plugin for using arbitrary LV2 instruments inside LMMS."), + "Johannes Lorenz ", + 0x0100, + Plugin::Instrument, + new PluginPixmapLoader("logo"), + nullptr, + new Lv2SubPluginFeatures(Plugin::Instrument) +}; + + + + +/* + Lv2Instrument +*/ + + +Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, + Descriptor::SubPluginFeatures::Key *key) : + Instrument(instrumentTrackArg, &lv2instrument_plugin_descriptor, key), + Lv2ControlBase(this, key->attributes["uri"]) +{ + if(Lv2ControlBase::isValid()) + { +#ifdef LV2_INSTRUMENT_USE_MIDI + for (int i = 0; i < NumKeys; ++i) { + m_runningNotes[i] = 0; + } +#endif + connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()), + this, SLOT(updatePitchRange())); + connect(Engine::mixer(), SIGNAL(sampleRateChanged()), + this, SLOT(reloadPlugin())); + if(multiChannelLinkModel()) { + connect(multiChannelLinkModel(), SIGNAL(dataChanged()), + this, SLOT(updateLinkStatesFromGlobal())); + connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), + this, SLOT(linkPort(int, bool))); + } + + // now we need a play-handle which cares for calling play() + InstrumentPlayHandle *iph = + new InstrumentPlayHandle(this, instrumentTrackArg); + Engine::mixer()->addPlayHandle(iph); + } +} + + + + +Lv2Instrument::~Lv2Instrument() +{ + Engine::mixer()->removePlayHandlesOfTypes(instrumentTrack(), + PlayHandle::TypeNotePlayHandle | + PlayHandle::TypeInstrumentPlayHandle); +} + + + + +bool Lv2Instrument::isValid() const { return Lv2ControlBase::isValid(); } + + + + +void Lv2Instrument::saveSettings(QDomDocument &doc, QDomElement &that) +{ + Lv2ControlBase::saveSettings(doc, that); +} + + + + +void Lv2Instrument::loadSettings(const QDomElement &that) +{ + Lv2ControlBase::loadSettings(that); +} + + + + +void Lv2Instrument::loadFile(const QString &file) +{ + Lv2ControlBase::loadFile(file); +} + + + + +#ifdef LV2_INSTRUMENT_USE_MIDI +bool Lv2Instrument::handleMidiEvent( + const MidiEvent &event, const MidiTime &time, f_cnt_t offset) +{ + (void)time; + (void)offset; +#ifdef TODO + switch (event.type()) + { + // the old zynaddsubfx plugin always uses channel 0 + case MidiNoteOn: + if (event.velocity() > 0) + { + if (event.key() <= 0 || event.key() >= 128) + { + break; + } + if (m_runningNotes[event.key()] > 0) + { + m_pluginMutex.lock(); + writeOsc("/noteOff", "ii", 0, event.key()); + m_pluginMutex.unlock(); + } + ++m_runningNotes[event.key()]; + m_pluginMutex.lock(); + writeOsc("/noteOn", "iii", 0, event.key(), + event.velocity()); + m_pluginMutex.unlock(); + } + break; + case MidiNoteOff: + if (event.key() > 0 && event.key() < 128) { + if (--m_runningNotes[event.key()] <= 0) + { + m_pluginMutex.lock(); + writeOsc("/noteOff", "ii", 0, event.key()); + m_pluginMutex.unlock(); + } + } + break; + /* case MidiPitchBend: + m_master->SetController( event.channel(), + C_pitchwheel, event.pitchBend()-8192 ); break; case + MidiControlChange: m_master->SetController( event.channel(), + midiIn.getcontroller( + event.controllerNumber() ), event.controllerValue() ); + break;*/ + default: + break; + } +#else + (void)event; +#endif + // those can be called from GUI threads while the plugin is running, + // so this requires caching, e.g. in ringbuffers + return true; +} +#endif + + + + +// not yet working +#ifndef LV2_INSTRUMENT_USE_MIDI +void Lv2Instrument::playNote(NotePlayHandle *nph, sampleFrame *) +{ + // no idea what that means + if (nph->isMasterNote() || (nph->hasParent() && nph->isReleased())) + { + return; + } + + const f_cnt_t tfp = nph->totalFramesPlayed(); + + const float LOG440 = 2.643452676f; + + int midiNote = (int)floor( + 12.0 * (log2(nph->unpitchedFrequency()) - LOG440) - 4.0); + + qDebug() << "midiNote: " << midiNote << ", r? " << nph->isReleased(); + // out of range? + if (midiNote <= 0 || midiNote >= 128) + { + return; + } + + if (tfp == 0) + { + const int baseVelocity = + instrumentTrack()->midiPort()->baseVelocity(); + m_plugin->send_osc("/noteOn", "iii", 0, midiNote, baseVelocity); + } + else if (nph->isReleased() && + !nph->instrumentTrack() + ->isSustainPedalPressed()) // note is released during + // this period + { + m_plugin->send_osc("/noteOff", "ii", 0, midiNote); + } + else if (nph->framesLeft() <= 0) + { + m_plugin->send_osc("/noteOff", "ii", 0, midiNote); + } + // those can be called from GUI threads while the plugin is running, + // so this requires caching, e.g. in ringbuffers +} +#endif + + + + +void Lv2Instrument::play(sampleFrame *buf) +{ + //if (m_plugin) + { + copyModelsFromLmms(); + + fpp_t fpp = Engine::mixer()->framesPerPeriod(); + + run(static_cast(fpp)); + + copyBuffersToLmms(buf, fpp); + } + instrumentTrack()->processAudioBuffer( + buf, Engine::mixer()->framesPerPeriod(), nullptr); +} + + + + +PluginView *Lv2Instrument::instantiateView(QWidget *parent) +{ + return new Lv2InsView(this, parent); +} + + + + +void Lv2Instrument::updatePitchRange() +{ + qDebug() << "Lmms: Cannot update pitch range for lv2 plugin:" + "not implemented yet"; +} + + + + +void Lv2Instrument::reloadPlugin() { Lv2ControlBase::reloadPlugin(); } + + + + +void Lv2Instrument::updateLinkStatesFromGlobal() +{ + Lv2ControlBase::updateLinkStatesFromGlobal(); +} + + + + +QString Lv2Instrument::nodeName() const +{ + return Lv2ControlBase::nodeName(); +} + + + + +DataFile::Types Lv2Instrument::settingsType() +{ + return DataFile::InstrumentTrackSettings; +} + + + + +void Lv2Instrument::setNameFromFile(const QString &name) +{ + instrumentTrack()->setName(name); +} + + + + +/* + Lv2InsView +*/ + + +Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : + InstrumentView(_instrument, _parent), + Lv2ViewBase(this, _instrument) +{ + setAutoFillBackground(true); + if(m_reloadPluginButton) { + connect(m_reloadPluginButton, SIGNAL(toggled(bool)), + this, SLOT(reloadPlugin())); + } + if(m_toggleUIButton) { + connect(m_toggleUIButton, SIGNAL(toggled(bool)), + this, SLOT(toggleUI())); + } + if(m_helpButton) { + connect(m_helpButton, SIGNAL(toggled(bool)), + this, SLOT(toggleHelp(bool))); + } +} + + + + +Lv2InsView::~Lv2InsView() +{ + Lv2Instrument *model = castModel(); + if (model && /* DISABLES CODE */ (false) + /* TODO: check if plugin has UI extension */ && model->hasGui()) + { + qDebug() << "shutting down UI..."; + // TODO: tell plugin to hide the UI + } +} + + + + +void Lv2InsView::dragEnterEvent(QDragEnterEvent *_dee) +{ + void (QDragEnterEvent::*reaction)(void) = &QDragEnterEvent::ignore; + + if (_dee->mimeData()->hasFormat(StringPairDrag::mimeType())) + { + const QString txt = + _dee->mimeData()->data(StringPairDrag::mimeType()); + if (txt.section(':', 0, 0) == "pluginpresetfile") { + reaction = &QDragEnterEvent::acceptProposedAction; + } + } + + (_dee->*reaction)(); +} + + + + +void Lv2InsView::dropEvent(QDropEvent *_de) +{ + const QString type = StringPairDrag::decodeKey(_de); + const QString value = StringPairDrag::decodeValue(_de); + if (type == "pluginpresetfile") + { + castModel()->loadFile(value); + _de->accept(); + return; + } + _de->ignore(); +} + + + + +void Lv2InsView::reloadPlugin() +{ + Lv2Instrument *model = castModel(); + model->reloadPlugin(); +} + + + + +void Lv2InsView::toggleUI() +{ + Lv2Instrument *model = castModel(); + if (/* DISABLES CODE */ (false) + /* TODO: check if plugin has the UI extension */ && + model->hasGui() != m_toggleUIButton->isChecked()) + { + model->setHasGui(m_toggleUIButton->isChecked()); + // TODO: show the UI + ControllerConnection::finalizeConnections(); + } +} + + + + +void Lv2InsView::toggleHelp(bool visible) +{ + Lv2ViewBase::toggleHelp(visible); +} + + + + +void Lv2InsView::modelChanged() +{ + Lv2ViewBase::modelChanged(castModel()); +} + + + + +extern "C" +{ + +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) +{ + using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; + Lv2Instrument* ins = new Lv2Instrument( + static_cast(_parent), + static_cast(_data )); + if(!ins->isValid()) + ins = nullptr; + return ins; +} + +} diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h new file mode 100644 index 00000000000..4acdd419367 --- /dev/null +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -0,0 +1,130 @@ +/* + * Lv2Instrument.h - implementation of LV2 instrument + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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_INSTRUMENT_H +#define LV2_INSTRUMENT_H + +#include +#include +#include + +#include "Instrument.h" +#include "InstrumentView.h" +#include "Note.h" +#include "Lv2ControlBase.h" +#include "Lv2ViewBase.h" + +// whether to use MIDI vs playHandle +// currently only MIDI works +#define LV2_INSTRUMENT_USE_MIDI + +class QPushButton; + + +class Lv2Instrument : public Instrument, public Lv2ControlBase +{ + Q_OBJECT +public: + /* + initialization + */ + Lv2Instrument(InstrumentTrack *instrumentTrackArg, + Descriptor::SubPluginFeatures::Key* key); + ~Lv2Instrument() override; + //! Must be checked after ctor or reload + bool isValid() const; + + /* + load/save + */ + void saveSettings(QDomDocument &doc, QDomElement &that) override; + void loadSettings(const QDomElement &that) override; + void loadFile(const QString &file) override; + + /* + realtime funcs + */ + bool hasNoteInput() const override { return false; /* not supported yet */ } +#ifdef LV2_INSTRUMENT_USE_MIDI + bool handleMidiEvent(const MidiEvent &event, + const MidiTime &time = MidiTime(), f_cnt_t offset = 0) override; +#else + void playNote(NotePlayHandle *nph, sampleFrame *) override; +#endif + void play(sampleFrame *buf) override; + + /* + misc + */ + Flags flags() const override + { +#ifdef LV2_INSTRUMENT_USE_MIDI + return IsSingleStreamed | IsMidiBased; +#else + return IsSingleStreamed; +#endif + } + PluginView *instantiateView(QWidget *parent) override; + +private slots: + void updatePitchRange(); + void reloadPlugin(); + void updateLinkStatesFromGlobal(); + void linkPort(int id, bool state) { Lv2ControlBase::linkPort(id, state); } + +private: + QString nodeName() const override; + DataFile::Types settingsType() override; + void setNameFromFile(const QString &name) override; + +#ifdef LV2_INSTRUMENT_USE_MIDI + int m_runningNotes[NumKeys]; +#endif + + friend class Lv2InsView; +}; + + +class Lv2InsView : public InstrumentView, public Lv2ViewBase +{ + Q_OBJECT +public: + Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent); + virtual ~Lv2InsView(); + +protected: + virtual void dragEnterEvent(QDragEnterEvent *_dee); + virtual void dropEvent(QDropEvent *_de); + +private slots: + void reloadPlugin(); + void toggleUI(); + void toggleHelp(bool visible); + +private: + void modelChanged(); +}; + + +#endif // LV2_INSTRUMENT_H diff --git a/plugins/Lv2Instrument/logo.png b/plugins/Lv2Instrument/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c423ccea45839a6296fa79d972484b2fad7c0f51 GIT binary patch literal 967 zcmV;&133JNP)aE)q(*?>IU8e zs(@dBpMl9`4&Wi631|fV0QQ*KXGH$|MoANrew5TwYE+Tb>b^}#YK&F*T+$6mb*0L8 zN$Vutlyo6ftd=w->2Rq}T1iLT*VO>!E^qe9`d!4J-gV~h-^}c0Lcbn(G+~lAvynMC zQV&!lM1Pvu6-kxJ_9OV#{b&Y;rW@?N>Jy`KtCy|CdK($N%sP~K1nrh zKP>58NsmWt|L5ysERh32xg6M$VzUFtMcR4M?Hho-z$Hmf&eqU<2ViESz*m8IXNuWN zL3zk&HwqjCzI6Fxz_BF_pui|>lGIdyK21PdAnx~{aoWrdn^_0&q3b$}5x~blevYFM z%JXg?;s<}{mOKD6y8#Ra;$BH*!QU?fdV_jr%xpZUR|V|x;@x75K#WG~fM)_(r^{#1 zC*k}Pzp8iZuc;8a)E^`1kX{j-8~>0pKnFp74xwubKUo*5RTCU}i&} zyT@n~(r3iXzLvBNcstM?k~HSem12$n!23bDOHwPay+FO!1NjPIZSWLC`2rkllhji{ z_mex;mpCltfR7@54S3FU8~AjV{-)-VqRuCon+<2(?u(Rq7w;pTUi4%xiS3eVBa=C8 zvM(5m9YD@A%~QUb#PD~4)L}s=Pr0QKaBH4zcAMEzNuL1S5&dAwfn{DdDboR5_jg^e zEanp%%Y2vAT}yPToK8iqB`I{-hqmJZxXaq=^t>F-?tVWkx0mpNevA8fGN!EZo$rpM zS0z=KkmJ>o_PK9ko^D5P=IVhHz|$`JJ#tVfHY$Kx|Go&k?8n&Xs)M8)@GP(yc#6`v p8yyC|0nVA(6d5vP$dDm}<6kPu9i$iGsP6y(002ovPDHLkV1fcwztR8z literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37da8f414f9..c98c3655754 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,6 +89,18 @@ IF(NOT ("${LAME_INCLUDE_DIRS}" STREQUAL "")) INCLUDE_DIRECTORIES("${LAME_INCLUDE_DIRS}") ENDIF() +IF(NOT ("${LV2_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) +ENDIF() + +IF(NOT ("${LILV_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) +ENDIF() + +IF(NOT ("${SUIL_INCLUDE_DIRS}" STREQUAL "")) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) +ENDIF() + # Use libraries in non-standard directories (e.g., another version of Qt) IF(LMMS_BUILD_LINUX) LINK_LIBRARIES(-Wl,--enable-new-dtags) @@ -164,6 +176,9 @@ SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} ${JACK_LIBRARIES} ${OGGVORBIS_LIBRARIES} ${LAME_LIBRARIES} + ${LV2_LIBRARIES} + ${SUIL_LIBRARIES} + ${LILV_LIBRARIES} ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${EXTRA_LIBRARIES} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 29ff423bb97..a4bc08c5e30 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -52,6 +52,7 @@ set(LMMS_SRCS core/Plugin.cpp core/PluginIssue.cpp core/PluginFactory.cpp + core/PluginIssue.cpp core/PresetPreviewPlayHandle.cpp core/ProjectJournal.cpp core/ProjectRenderer.cpp @@ -88,6 +89,12 @@ set(LMMS_SRCS core/audio/AudioSampleRecorder.cpp core/audio/AudioSdl.cpp + core/lv2/Lv2ControlBase.cpp + core/lv2/Lv2Ports.cpp + core/lv2/Lv2Proc.cpp + core/lv2/Lv2Manager.cpp + core/lv2/Lv2SubPluginFeatures.cpp + core/midi/MidiAlsaRaw.cpp core/midi/MidiAlsaSeq.cpp core/midi/MidiClient.cpp diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 129c9738b3e..44cb920d844 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -166,6 +166,9 @@ bool DataFile::validate( QString extension ) ( extension == "xiz" && ! pluginFactory->pluginSupportingExtension(extension).isNull()) || extension == "sf2" || extension == "sf3" || extension == "pat" || extension == "mid" || extension == "dll" +#ifdef LMMS_HAVE_LV2 + || extension == "lv2" +#endif ) ) { return true; diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index 50e25b0b416..b921f1aeae3 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -28,6 +28,7 @@ #include "ConfigManager.h" #include "FxMixer.h" #include "Ladspa2LMMS.h" +#include "Lv2Manager.h" #include "Mixer.h" #include "Plugin.h" #include "PresetPreviewPlayHandle.h" @@ -41,6 +42,9 @@ FxMixer * LmmsCore::s_fxMixer = NULL; BBTrackContainer * LmmsCore::s_bbTrackContainer = NULL; Song * LmmsCore::s_song = NULL; ProjectJournal * LmmsCore::s_projectJournal = NULL; +#ifdef LMMS_HAVE_LV2 +Lv2Manager * LmmsCore::s_lv2Manager = nullptr; +#endif Ladspa2LMMS * LmmsCore::s_ladspaManager = NULL; void* LmmsCore::s_dndPluginKey = nullptr; DummyTrackContainer * LmmsCore::s_dummyTC = NULL; @@ -63,6 +67,10 @@ void LmmsCore::init( bool renderOnly ) s_fxMixer = new FxMixer; s_bbTrackContainer = new BBTrackContainer; +#ifdef LMMS_HAVE_LV2 + s_lv2Manager = new Lv2Manager; + s_lv2Manager->initPlugins(); +#endif s_ladspaManager = new Ladspa2LMMS; s_projectJournal->setJournalling( true ); @@ -95,6 +103,9 @@ void LmmsCore::destroy() deleteHelper( &s_fxMixer ); deleteHelper( &s_mixer ); +#ifdef LMMS_HAVE_LV2 + deleteHelper( &s_lv2Manager ); +#endif deleteHelper( &s_ladspaManager ); //delete ConfigManager::inst(); diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp new file mode 100644 index 00000000000..74e1b1a96f0 --- /dev/null +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -0,0 +1,327 @@ +/* + * Lv2ControlBase.cpp - Lv2 control base class + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2ControlBase.h" + +#ifdef LMMS_HAVE_LV2 + +#include "AutomatableModel.h" +#include "Engine.h" +#include "Lv2Manager.h" +#include "Lv2Proc.h" + + + + +Plugin::PluginTypes Lv2ControlBase::check(const LilvPlugin *plugin, + std::vector &issues, bool printIssues) +{ + // for some reason, all checks can be done by one processor... + return Lv2Proc::check(plugin, issues, printIssues); +} + + + + +Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : + m_plugin(Engine::getLv2Manager()->getPlugin(uri)) +{ + if(m_plugin) + { + int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo + while (channelsLeft > 0) + { + Lv2Proc* newOne = new Lv2Proc(m_plugin, that); + if(newOne->isValid()) + { + channelsLeft -= std::max( + 1 + static_cast(newOne->inPorts().m_right), + 1 + static_cast(newOne->outPorts().m_right)); + Q_ASSERT(channelsLeft >= 0); + m_procs.push_back(newOne); + } + else + { + qCritical() << "Failed instantiating LV2 processor"; + m_valid = false; + channelsLeft = 0; + } + } + if(m_valid) + { + m_channelsPerProc = DEFAULT_CHANNELS / m_procs.size(); + if(m_procs.size() > 1) + { + m_procs[0]->makeLinkingProc(); + createMultiChannelLinkModel(); + } + + // initially link all controls + for(int i = 0; i < static_cast(m_procs[0]->controlCount()); + ++i) { + linkPort(i, true); + } + } + } + else { + qCritical() << "No Lv2 plugin found for URI" << uri; + m_valid = false; + } +} + + + + +Lv2ControlBase::~Lv2ControlBase() +{ + for(Lv2Proc* c : m_procs) { delete c; } +} + + + + +LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) +{ + return (m_procs.size() > idx) ? m_procs[idx] : nullptr; +} + + + + +void Lv2ControlBase::copyModelsFromLmms() { + for(Lv2Proc* c : m_procs) { c->copyModelsFromCore(); } +} + + + + +void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { + unsigned offset = 0; + for(Lv2Proc* c : m_procs) { + c->copyBuffersFromCore(buf, offset, m_channelsPerProc, frames); + offset += m_channelsPerProc; + } +} + + + + +void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { + unsigned offset = 0; + for(const Lv2Proc* c : m_procs) { + c->copyBuffersToCore(buf, offset, m_channelsPerProc, frames); + offset += m_channelsPerProc; + } +} + + + + +void Lv2ControlBase::run(unsigned frames) { + for(Lv2Proc* c : m_procs) { c->run(frames); } +} + + + + +void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) +{ +#ifdef TODO + // save internal data? + if (m_lv2Plugin->save_has()) + { + QTemporaryFile tf; + if (tf.open()) + { + const std::string fn = QSTR_TO_STDSTR( + QDir::toNativeSeparators(tf.fileName())); + m_plugin->save(fn.c_str(), ++m_saveTicket); + + while (!m_plugin->save_check(fn.c_str(), m_saveTicket)) { + QThread::msleep(1); + } + + QDomCDATASection cdata = doc.createCDATASection( + QString::fromUtf8(tf.readAll())); + that.appendChild(cdata); + } + tf.remove(); + } + + // save connected models + if (m_connectedModels.size()) + { + QDomElement newNode = doc.createElement("connected-models"); + QMap::const_iterator i = + m_connectedModels.constBegin(); + while (i != m_connectedModels.constEnd()) + { + i.value()->saveSettings(doc, newNode, i.key()); + ++i; + } + + that.appendChild(newNode); + } +#else + (void)doc; + (void)that; +#endif +} + + + + +void Lv2ControlBase::loadSettings(const QDomElement &that) +{ +#ifdef TODO + if (!that.hasChildNodes()) + { + return; + } + + for (QDomNode node = that.firstChild(); !node.isNull(); + node = node.nextSibling()) + { + QDomCDATASection cdata = node.toCDATASection(); + QDomElement elem; + // load internal state? + if (!cdata.isNull() && m_lv2Plugin->load_has()) + { + QTemporaryFile tf; + tf.setAutoRemove(false); + if (tf.open()) + { + tf.write(cdata.data().toUtf8()); + tf.flush(); + loadFileInternal(tf.fileName()); + } + } + // load connected models? + else if (node.nodeName() == "connected-models" && + !(elem = node.toElement()).isNull()) + { + QDomNamedNodeMap attrs = elem.attributes(); + + auto do_load = [&](const QString &name, + QDomElement elem) { + AutomatableModel *m = modelAtPort(name); + // this will automatically + // load any "connection" node: + m->loadSettings(elem, name); + m_connectedModels[name] = m; + }; + + for (int i = 0; i < attrs.count(); ++i) + { + QDomAttr attribute = attrs.item(i).toAttr(); + do_load(attribute.name(), elem); + } + + for (QDomElement portnode = elem.firstChildElement(); + !portnode.isNull(); + portnode = portnode.nextSiblingElement()) + { + if (portnode.nodeName() != "connection") + { + QString name = portnode.nodeName(); + if (name == "automatablemodel") { + name = portnode.attribute( + "nodename"); + } + do_load(name, elem); + } + } + } + } +#else + (void)that; +#endif +} + + + + +void Lv2ControlBase::loadFile(const QString &file) +{ +#ifdef TODO + loadFileInternal(file); + setNameFromFile(QFileInfo(file).baseName().replace( + QRegExp("^[0-9]{4}-"), QString())); +#else + (void)file; +#endif +} + + + + +void Lv2ControlBase::reloadPlugin() +{ +#ifdef TODO + // refresh ports that are only read on restore + m_ports.samplerate = Engine::mixer()->processingSampleRate(); + int16_t fpp = Engine::mixer()->framesPerPeriod(); + assert(fpp >= 0); + m_ports.buffersize = static_cast(fpp); + + if (m_lv2Plugin->restore_has()) + { + // use the offered restore function + m_plugin->restore(++m_restoreTicket); + + while (!m_plugin->restore_check(m_restoreTicket)) { + QThread::msleep(1); + } + } + else + { + // save state of current plugin instance + DataFile m(spe()); + + saveSettings(m, m.content()); + + shutdownPlugin(); + // init plugin (will create a new instance) + initPlugin(); + + // and load the settings again + loadSettings(m.content()); + } +#endif +} + + + + +std::size_t Lv2ControlBase::controlCount() const { + std::size_t res = 0; + for(const Lv2Proc* c : m_procs) + res += c->controlCount(); + return res; +} + + + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp new file mode 100644 index 00000000000..fb3266ce3d4 --- /dev/null +++ b/src/core/lv2/Lv2Manager.cpp @@ -0,0 +1,128 @@ +/* + * Lv2Manager.cpp - Implementation of Lv2Manager class + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2Manager.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include + +#include + +#include "ConfigManager.h" +#include "Plugin.h" +#include "PluginFactory.h" +#include "Lv2ControlBase.h" +#include "PluginIssue.h" + + + + +Lv2Manager::Lv2Manager() +{ + m_world = lilv_world_new(); + lilv_world_load_all(m_world); +} + + + + +Lv2Manager::~Lv2Manager() +{ +} + + + + +const LilvPlugin *Lv2Manager::getPlugin(const std::string &uri) +{ + auto itr = m_lv2InfoMap.find(uri); + return itr == m_lv2InfoMap.end() ? nullptr : itr->second.plugin(); +} + + + + +const LilvPlugin *Lv2Manager::getPlugin(const QString uri) +{ + return getPlugin(std::string(uri.toUtf8())); +} + + + + +void Lv2Manager::initPlugins() +{ + const LilvPlugins* plugins = lilv_world_get_all_plugins(m_world); + + LILV_FOREACH(plugins, itr, plugins) + { + const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); + + std::vector issues; + Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues, true); + Lv2Info info(curPlug, type, issues.empty()); + + m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] + = std::move(info); + } +} + + + + +// unused + untested yet +bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) +{ + const LilvPluginClasses* allClasses = lilv_world_get_plugin_classes(m_world); + const LilvPluginClass* root = lilv_world_get_plugin_class(m_world); + const LilvPluginClass* gen = lilv_plugin_classes_get_by_uri(allClasses, + uri(uriStr).get()); + + // lv2:Generator is what can be generating an LMMS instrument track + // lv2:Instrument is lv:Generator with MIDI/piano input + // => LMMS "Instrument" corresponds to lv2:Generator + auto clssEq = [](const LilvPluginClass* pc1, + const LilvPluginClass* pc2) -> bool + { + return lilv_node_equals( + lilv_plugin_class_get_uri(pc1), + lilv_plugin_class_get_uri(pc2)); + }; + bool isGen = false; + for (; + clssEq(clvss, root) && (isGen = clssEq(clvss, gen)); + clvss = lilv_plugin_classes_get_by_uri(allClasses, + lilv_plugin_class_get_parent_uri(clvss)) + ) ; + return isGen; +} + + + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp new file mode 100644 index 00000000000..9a285ffbf97 --- /dev/null +++ b/src/core/lv2/Lv2Ports.cpp @@ -0,0 +1,259 @@ +/* + * Lv2Ports.cpp - Lv2 port implementation + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "Lv2Ports.h" + +#ifdef LMMS_HAVE_LV2 + +#include "Engine.h" +#include "Lv2Basics.h" +#include "Lv2Manager.h" + +namespace Lv2Ports { + + + + +const char *toStr(Flow pf) +{ + switch(pf) + { + case Flow::Unknown: return "unknown"; + case Flow::Input: return "input"; + case Flow::Output: return "output"; + } + return ""; +} + + + + +const char *toStr(Type pt) +{ + switch(pt) + { + case Type::Unknown: return "unknown"; + case Type::Control: return "control"; + case Type::Audio: return "audio"; + case Type::Event: return "event"; + case Type::Cv: return "cv"; + } + return ""; +} + + + + +const char *toStr(Vis pv) +{ + switch(pv) + { + case Vis::Toggled: return "toggled"; + case Vis::Enumeration: return "enumeration"; + case Vis::Integer: return "integer"; + case Vis::None: return "none"; + } + return ""; +} + + + + +std::vector Meta::get(const LilvPlugin *plugin, + unsigned int portNum) +{ + std::vector portIssues; + auto issue = [&portIssues](PluginIssueType i, const char* msg = "") { + portIssues.emplace_back(i, msg); }; + + Lv2Manager* man = Engine::getLv2Manager(); + AutoLilvNode connectionOptional = man->uri(LV2_CORE__connectionOptional); + + const LilvPort* lilvPort = lilv_plugin_get_port_by_index(plugin, portNum); + + auto portFunc = [&plugin, &lilvPort, &man]( + bool (*fptr)(const LilvPlugin*, const LilvPort*, const LilvNode*), + const char* str) { + bool res = fptr(plugin, lilvPort, man->uri(str).get()); + return res; + }; + + auto hasProperty = [&portFunc](const char* str) { + return portFunc(lilv_port_has_property, str); }; + auto isA = [&portFunc](const char* str) { + return portFunc(lilv_port_is_a, str); }; + + + const char* portName = lilv_node_as_string(lilv_port_get_name(plugin, lilvPort)); + + m_optional = lilv_port_has_property(plugin, lilvPort, + connectionOptional.get()); + + m_vis = hasProperty(LV2_CORE__integer) + ? Vis::Integer // WARNING: this may still be changed below + : hasProperty(LV2_CORE__enumeration) + ? Vis::Enumeration + : hasProperty(LV2_CORE__toggled) + ? Vis::Toggled + : Vis::None; + + if (isA(LV2_CORE__InputPort)) { + m_flow = Flow::Input; + } + else if (isA(LV2_CORE__OutputPort)) { + m_flow = Flow::Output; + } else { + m_flow = Flow::Unknown; + issue(unknownPortFlow, portName); + } + + m_def = .0f; m_min = .0f; m_max = .0f; + + if (isA(LV2_CORE__ControlPort)) { + m_type = Type::Control; + + if(m_flow == Flow::Input) + { + bool isToggle = m_vis == Vis::Toggled; + + LilvNode * defN, * minN = nullptr, * maxN = nullptr; + lilv_port_get_range(plugin, lilvPort, &defN, + isToggle ? nullptr : &minN, + isToggle ? nullptr : &maxN); + + auto takeRangeValue = [&](LilvNode* node, + float& storeHere, PluginIssueType it) { + if(node) { + storeHere = lilv_node_as_float(node); + } + else { + issue(it, portName); + } + lilv_node_free(node); + }; + + takeRangeValue(defN, m_def, portHasNoDef); + if(!isToggle) + { + takeRangeValue(minN, m_min, portHasNoMin); + takeRangeValue(maxN, m_max, portHasNoMax); + + if(m_max - m_min > 15.0f) + { + // range too large for spinbox visualisation, use knobs + // e.g. 0...15 would be OK + m_vis = Vis::None; + } + } + } + } + else if (isA(LV2_CORE__AudioPort)) { + m_type = Type::Audio; + } else if (isA(LV2_CORE__CVPort)) { + issue(badPortType, "cvPort"); + m_type = Type::Cv; + } else { + if (m_optional) { + m_used = false; + } + else { + issue(PluginIssueType::unknownPortType, portName); + m_type = Type::Unknown; + } + } + + return portIssues; +} + + + + +QString PortBase::name() const +{ + AutoLilvNode node = lilv_port_get_name(m_plugin, m_port); + QString res = lilv_node_as_string(node.get()); + return res; +} + + + + +Audio::Audio(std::size_t bufferSize, bool isSidechain) + : m_buffer(bufferSize), m_sidechain(isSidechain) +{ +} + + + + +void Audio::copyBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) +{ + for (std::size_t f = 0; f < static_cast(frames); ++f) + { + m_buffer[f] = lmmsBuf[f][channel]; + } +} + + + + +void Audio::addBuffersFromCore(const sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) +{ + for (std::size_t f = 0; f < static_cast(frames); ++f) + { + m_buffer[f] = (m_buffer[f] + lmmsBuf[f][channel]) / 2.0f; + } +} + + + + +void Audio::copyBuffersToCore(sampleFrame *lmmsBuf, + unsigned channel, fpp_t frames) const +{ + for (std::size_t f = 0; f < static_cast(frames); ++f) + { + lmmsBuf[f][channel] = m_buffer[f]; + } +} + + + + +// make the compiler happy, give each class with virtuals +// a function (the destructor here) which is in a cpp file +PortBase::~PortBase() {} +ConstVisitor::~ConstVisitor() {} +Visitor::~Visitor() {} + + + + +} // namespace Lv2Ports + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp new file mode 100644 index 00000000000..a19d8cb2f8c --- /dev/null +++ b/src/core/lv2/Lv2Proc.cpp @@ -0,0 +1,578 @@ +/* + * Lv2Proc.cpp - Lv2 processor class + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "Lv2Proc.h" + +#ifdef LMMS_HAVE_LV2 + +#include + +#include "AutomatableModel.h" +#include "ComboBoxModel.h" +#include "Engine.h" +#include "Lv2Manager.h" +#include "Lv2Ports.h" +#include "Mixer.h" + + + + +Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, + std::vector& issues, bool printIssues) +{ + unsigned maxPorts = lilv_plugin_get_num_ports(plugin); + enum { checkIn, checkOut, checkMax }; + unsigned audioChannels[checkMax] = { 0, 0 }; // input and output count + + for(unsigned portNum = 0; portNum < maxPorts; ++portNum) + { + Lv2Ports::Meta meta; + // does all port checks: + std::vector tmp = meta.get(plugin, portNum); + std::move(tmp.begin(), tmp.end(), std::back_inserter(issues)); + + if(meta.m_type == Lv2Ports::Type::Audio && + !portIsSideChain(plugin, + lilv_plugin_get_port_by_index(plugin, portNum))) + ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output]; + } + + if(audioChannels[checkIn] > 2) + issues.emplace_back(tooManyInputChannels, + std::to_string(audioChannels[0])); + if(audioChannels[checkOut] == 0) + issues.emplace_back(noOutputChannel); + else if(audioChannels[checkOut] > 2) + issues.emplace_back(tooManyOutputChannels, + std::to_string(audioChannels[1])); + + const LilvNodes* reqFeats = lilv_plugin_get_required_features(plugin); + LILV_FOREACH(nodes, itr, reqFeats) + { + issues.emplace_back(featureNotSupported, + lilv_node_as_string(lilv_nodes_get(reqFeats, itr))); + } + + if(printIssues && issues.size()) + { + qDebug() << "Lv2 plugin " << + lilv_node_as_string(lilv_plugin_get_name(plugin)) + << " can not be loaded:"; + for(const PluginIssue& iss : issues) { qDebug() << " - " << iss; } + } + + return (audioChannels[1] > 2 || audioChannels[0] > 2) + ? Plugin::Undefined + : audioChannels[0] + ? Plugin::Effect + : Plugin::Instrument; +} + + + + +Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : + LinkedModelGroup(parent), + m_plugin(plugin) +{ + if(m_plugin) + { + initPlugin(); + } + else + { + qCritical() << ":-( ! No descriptor found for" << plugin; + m_valid = false; + } +} + + + + +Lv2Proc::~Lv2Proc() { shutdownPlugin(); } + + + + +void Lv2Proc::dumpPorts() +{ + std::size_t num = 0; + for(const Lv2Ports::PortBase* port: m_ports) + { + (void)port; + dumpPort(num++); + } +} + + + + +void Lv2Proc::copyModelsFromCore() +{ + struct FloatFromModel : public ConstModelVisitor + { + const std::vector* m_scalePointMap; // in + float m_res; // out + void visit(const FloatModel& m) override { m_res = m.value(); } + void visit(const IntModel& m) override { + m_res = static_cast(m.value()); } + void visit(const BoolModel& m) override { + m_res = static_cast(m.value()); } + void visit(const ComboBoxModel& m) override { + m_res = (*m_scalePointMap)[static_cast(m.value())]; } + }; + + struct Copy : public Lv2Ports::Visitor + { + void visit(Lv2Ports::Control& ctrl) override + { + if(ctrl.m_flow == Lv2Ports::Flow::Input) + { + FloatFromModel 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) + { + FloatFromModel 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); + } + } + } copy; + + for (Lv2Ports::PortBase* port : m_ports) + { + port->accept(copy); + } +} + + + + +void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, + unsigned offset, unsigned num, + fpp_t frames) +{ + inPorts().m_left->copyBuffersFromCore(buf, offset, frames); + if(num > 1) + { + // if the caller requests to take input from two channels, but we only + // have one input channel... take medium of left and right for + // mono input + // (this happens if we have two outputs and only one input) + if(inPorts().m_right) + inPorts().m_right->copyBuffersFromCore(buf, offset + 1, frames); + else + inPorts().m_left->addBuffersFromCore(buf, offset + 1, frames); + } +} + + + + +void Lv2Proc::copyBuffersToCore(sampleFrame* buf, + unsigned offset, unsigned num, + fpp_t frames) const +{ + outPorts().m_left->copyBuffersToCore(buf, offset + 0, frames); + if(num > 1) + { + // if the caller requests to copy into two channels, but we only have + // one output channel, duplicate our output + // (this happens if we have two inputs and only one output) + Lv2Ports::Audio* ap = outPorts().m_right + ? outPorts().m_right : outPorts().m_left; + ap->copyBuffersToCore(buf, offset + 1, frames); + } +} + + + + +void Lv2Proc::run(unsigned frames) +{ + lilv_instance_run(m_instance, static_cast(frames)); +} + + + + +AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) +{ + // unused currently + AutomatableModel *mod; + auto itr = m_connectedModels.find(uri.toUtf8().data()); + if (itr != m_connectedModels.end()) + { + mod = itr->second; + } + else + { +#if 0 + AutomatableModel *lv2Mod; + { + Lv2OscModelFactory vis(this, url.path()); + lv2::port_ref_base &base = + m_plugin->port(url.path().toUtf8().data()); + base.accept(vis); + lv2Mod = vis.m_res; + } + + if (lv2Mod) + { + m_connectedModels.insert(url.path(), lv2Mod); + mod = lv2Mod; + } + else + { +#endif + qDebug() << "LMMS: Could not create model from " + << "OSC port (received port\"" << uri + << "\", path \"" << uri << "\")"; + mod = nullptr; +#if 0 + } +#endif + } + return mod; +} + + + + +void Lv2Proc::initPlugin() +{ + createPorts(); + + m_instance = lilv_plugin_instantiate(m_plugin, + Engine::mixer()->processingSampleRate(), + nullptr); + + if(m_instance) + { + for(unsigned portNum = 0; portNum < m_ports.size(); ++portNum) + connectPort(portNum); + lilv_instance_activate(m_instance); + } + else + { + qCritical() << "Failed to create an instance of" + << lilv_node_as_string(lilv_plugin_get_name(m_plugin)); + m_valid = false; + } +} + + + + +void Lv2Proc::shutdownPlugin() +{ + lilv_instance_deactivate(m_instance); + m_instance = nullptr; +} + + + + +void Lv2Proc::loadFileInternal(const QString &file) +{ +#ifdef TODO + const QByteArray fn = file.toUtf8(); + m_plugin->load(fn.data(), ++m_saveTicket); + while (!m_plugin->load_check(fn.data(), m_saveTicket)) { + QThread::msleep(1); + } +#else + (void)file; +#endif +} + + + + +void Lv2Proc::createPort(unsigned portNum) +{ + Lv2Ports::PortBase*& port = m_ports[portNum]; + + Lv2Ports::Meta meta; + meta.get(m_plugin, portNum); + + const LilvPort* lilvPort = lilv_plugin_get_port_by_index(m_plugin, + portNum); + + if (meta.m_type == Lv2Ports::Type::Control) + { + Lv2Ports::Control* ctrl = new Lv2Ports::Control; + if(meta.m_flow == Lv2Ports::Flow::Input) + { + LilvNode* node = lilv_port_get_name(m_plugin, lilvPort); + QString dispName = lilv_node_as_string(node); + lilv_node_free(node); + switch(meta.m_vis) + { + case Lv2Ports::Vis::None: + { + // allow ~1000 steps + float stepSize = (meta.m_max - meta.m_min) / 1000.0f; + + // make multiples of 0.01 (or 0.1 for larger values) + float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; + stepSize -= fmodf(stepSize, minStep); + stepSize = std::max(stepSize, minStep); + + ctrl->m_connectedModel.reset( + new FloatModel(meta.m_def, meta.m_min, meta.m_max, + stepSize, nullptr, dispName)); + break; + } + case Lv2Ports::Vis::Integer: + ctrl->m_connectedModel.reset( + new IntModel(static_cast(meta.m_def), + static_cast(meta.m_min), + static_cast(meta.m_max), + nullptr, dispName)); + break; + case Lv2Ports::Vis::Enumeration: + { + ComboBoxModel* comboModel + = new ComboBoxModel( + nullptr, dispName); + const LilvScalePoints* sps = + lilv_port_get_scale_points(m_plugin, lilvPort); + LILV_FOREACH(scale_points, i, sps) + { + const LilvScalePoint* sp = lilv_scale_points_get(sps, i); + ctrl->m_scalePointMap.push_back(lilv_node_as_float( + lilv_scale_point_get_value(sp))); + comboModel->addItem( + lilv_node_as_string( + lilv_scale_point_get_label(sp))); + } + ctrl->m_connectedModel.reset(comboModel); + break; + } + case Lv2Ports::Vis::Toggled: + ctrl->m_connectedModel.reset( + new BoolModel(static_cast(meta.m_def), + nullptr, dispName)); + break; + } + } + port = ctrl; + } + else if (meta.m_type == Lv2Ports::Type::Audio) + { + Lv2Ports::Audio* audio = + new Lv2Ports::Audio( + static_cast( + Engine::mixer()->framesPerPeriod()), + portIsSideChain(m_plugin, lilvPort) + ); + port = audio; + } else { + port = new Lv2Ports::Unknown; + } + + *dynamic_cast(port) = meta; + port->m_port = lilvPort; + port->m_plugin = m_plugin; +} + + + + +void Lv2Proc::createPorts() +{ + // register ports at the processor after creation, + // i.e. link their data or count them + struct RegisterPort : public Lv2Ports::Visitor + { + Lv2Proc* proc; + + void visit(Lv2Ports::Control& ctrl) override + { + if(ctrl.m_flow == Lv2Ports::Flow::Input) + { + AutomatableModel* amo = ctrl.m_connectedModel.get(); + proc->m_connectedModels.emplace( + lilv_node_as_string(lilv_port_get_symbol( + proc->m_plugin, ctrl.m_port)), + amo); + proc->addModel(amo); + + ++proc->m_controlCount; + } + } + + void visit(Lv2Ports::Audio& audio) override + { + if(!audio.isSideChain()) + { + StereoPortRef* portRef; + StereoPortRef dummy; + switch(audio.m_flow) + { + case Lv2Ports::Flow::Input: + portRef = &proc->m_inPorts; + break; + case Lv2Ports::Flow::Output: + portRef = &proc->m_outPorts; + break; + case Lv2Ports::Flow::Unknown: + portRef = &dummy; + break; + } + // in Lv2, leftPort is defined to be the first port + if(!portRef->m_left) { + portRef->m_left = &audio; + } else if(!portRef->m_right) { + portRef->m_right = &audio; + } + } + } + }; + + unsigned maxPorts = lilv_plugin_get_num_ports(m_plugin); + m_ports.resize(maxPorts); + + for(unsigned portNum = 0; portNum < maxPorts; ++portNum) + { + createPort(portNum); + RegisterPort registerPort; + registerPort.proc = this; + m_ports[portNum]->accept(registerPort); + } + + // initially assign model values to port values + copyModelsFromCore(); + + // debugging: + //dumpPorts(); +} + + + + +struct ConnectPorts : public Lv2Ports::Visitor +{ + unsigned m_num; + LilvInstance* m_instance; + void con(void* location) { + lilv_instance_connect_port(m_instance, m_num, location); + } + void visit(Lv2Ports::Control& ctrl) override { con(&ctrl.m_val); } + void visit(Lv2Ports::Audio& audio) override { + con(audio.isSideChain() ? nullptr : audio.m_buffer.data()); } + void visit(Lv2Ports::Unknown&) override { con(nullptr); } + ~ConnectPorts() override; +}; + +ConnectPorts::~ConnectPorts() {} + +void Lv2Proc::connectPort(unsigned num) +{ + ConnectPorts connect; + connect.m_num = num; + connect.m_instance = m_instance; + m_ports[num]->accept(connect); +} + + + + +void Lv2Proc::destroyPorts() +{ + for(Lv2Ports::PortBase* p: m_ports) { delete p; } +} + + + + +void Lv2Proc::dumpPort(std::size_t num) +{ + struct DumpPortDetail : public Lv2Ports::ConstVisitor + { + void visit(const Lv2Ports::Control& ctrl) override { + qDebug() << " control port"; + // output ports may be uninitialized yet, only print inputs + if(ctrl.m_flow == Lv2Ports::Flow::Input) + { + qDebug() << " value:" << ctrl.m_val; + } + } + void visit(const Lv2Ports::Audio& audio) override { + qDebug() << (audio.isSideChain() ? " audio port (sidechain)" + : " audio port"); + qDebug() << " buffer size:" << audio.bufferSize(); + } + }; + + const Lv2Ports::PortBase& port = *m_ports[num]; + qDebug().nospace() << "port " << num << ":"; + qDebug() << " name:" + << lilv_node_as_string(lilv_port_get_name(m_plugin, port.m_port)); + qDebug() << " flow: " << Lv2Ports::toStr(port.m_flow); + qDebug() << " type: " << Lv2Ports::toStr(port.m_type); + qDebug() << " visualization: " << Lv2Ports::toStr(port.m_vis); + if(port.m_type == Lv2Ports::Type::Control || port.m_type == Lv2Ports::Type::Cv) + { + qDebug() << " default:" << port.m_def; + qDebug() << " min:" << port.m_min; + qDebug() << " max:" << port.m_max; + } + qDebug() << " optional: " << port.m_optional; + qDebug() << " => USED: " << port.m_used; + + DumpPortDetail dumper; + port.accept(dumper); +} + + + + +bool Lv2Proc::portIsSideChain(const LilvPlugin *plugin, const LilvPort *port) +{ + return lilv_port_has_property(plugin, port, + uri(LV2_CORE_PREFIX "isSidechain").get()) || + lilv_port_has_property(plugin, port, + uri(LV2_CORE__connectionOptional).get()); +} + + + + +AutoLilvNode Lv2Proc::uri(const char *uriStr) +{ + return Engine::getLv2Manager()->uri(uriStr); +} + + +#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp new file mode 100644 index 00000000000..c5200ae002d --- /dev/null +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -0,0 +1,199 @@ +/* + * Lv2SubPluginFeatures.cpp - derivation from + * Plugin::Descriptor::SubPluginFeatures for + * hosting LV2 plugins + * + * Copyright (c) 2006-2007 Danny McRae + * Copyright (c) 2006-2014 Tobias Doerffel + * + * 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 "Lv2SubPluginFeatures.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "AudioDevice.h" +#include "ConfigManager.h" +#include "Engine.h" +#include "Mixer.h" +#include "PluginFactory.h" +#include "Lv2Manager.h" +#include "embed.h" + + +const LilvPlugin *Lv2SubPluginFeatures::getPlugin( + const Plugin::Descriptor::SubPluginFeatures::Key &k) +{ + const LilvPlugin* result = Engine::getLv2Manager()-> + getPlugin(k.attributes["uri"]); + assert(result); + return result; +} + + + + +Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes _type) : + SubPluginFeatures(_type) +{ +} + + + + +void Lv2SubPluginFeatures::fillDescriptionWidget( + QWidget *_parent, const Key *k) const +{ + const LilvPlugin *plug = getPlugin(*k); + + QLabel *label = new QLabel(_parent); + label->setText(QWidget::tr("Name: ") + + lilv_node_as_string(lilv_plugin_get_name(plug))); + + QLabel *label2 = new QLabel(_parent); + label2->setText(QWidget::tr("URI: ") + + lilv_node_as_uri(lilv_plugin_get_uri(plug))); + + QWidget *maker = new QWidget(_parent); + QHBoxLayout *l = new QHBoxLayout(maker); + l->setMargin(0); + l->setSpacing(0); + + QLabel *maker_label = new QLabel(maker); + maker_label->setText(QWidget::tr("Maker: ")); + maker_label->setAlignment(Qt::AlignTop); + + QLabel *maker_content = new QLabel(maker); + maker_content->setText( + lilv_node_as_string(lilv_plugin_get_author_name(plug))); + maker_content->setWordWrap(true); + + l->addWidget(maker_label); + l->addWidget(maker_content, 1); + + QWidget *copyright = new QWidget(_parent); + l = new QHBoxLayout(copyright); + l->setMargin(0); + l->setSpacing(0); + copyright->setMinimumWidth(_parent->minimumWidth()); + + QLabel *copyright_label = new QLabel(copyright); + copyright_label->setText(QWidget::tr("Copyright: ")); + copyright_label->setAlignment(Qt::AlignTop); + + QLabel *copyright_content = new QLabel(copyright); + copyright_content->setText(""); + copyright_content->setWordWrap(true); + l->addWidget(copyright_label); + l->addWidget(copyright_content, 1); + + const LilvNodes* extensions = lilv_plugin_get_extension_data(plug); + (void)extensions; + +/* QLabel *requiresRealTime = new QLabel(_parent); + requiresRealTime->setText(QWidget::tr("Requires Real Time: ") + + ( + + ->properties.realtime_dependency ? QWidget::tr("Yes") + : QWidget::tr("No"))); + + QLabel *realTimeCapable = new QLabel(_parent); + realTimeCapable->setText(QWidget::tr("Real Time Capable: ") + + (plug->properties.hard_rt_capable ? QWidget::tr("Yes") + : QWidget::tr("No")));*/ + + // possibly TODO: version, project, plugin type, number of channels +} + + + + +QString Lv2SubPluginFeatures::additionalFileExtensions( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + (void)k; + // lv2 only loads .lv2 files + // maybe add conversions later, e.g. for loading xmz + return nullptr; +} + + + + +QString Lv2SubPluginFeatures::displayName( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + return lilv_node_as_string(lilv_plugin_get_name(getPlugin(k))); +} + + + + +QString Lv2SubPluginFeatures::description( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + (void)k; + return "description not implemented yet"; // TODO +} + + + + +const PixmapLoader *Lv2SubPluginFeatures::logo( + const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + (void)k; // TODO + return nullptr; +} + + + + +void Lv2SubPluginFeatures::listSubPluginKeys( + const Plugin::Descriptor *_desc, KeyList &_kl) const +{ + Lv2Manager *lv2Mgr = Engine::getLv2Manager(); + for (const std::pair &pr : + *lv2Mgr) + { + if (pr.second.type() == m_type && pr.second.isValid()) + { + using KeyType = + Plugin::Descriptor::SubPluginFeatures::Key; + KeyType::AttributeMap atm; + atm["uri"] = QString::fromUtf8(pr.first.c_str()); + const LilvPlugin* plug = pr.second.plugin(); + + + _kl.push_back(KeyType(_desc, + lilv_node_as_string(lilv_plugin_get_name(plug)), + atm)); + //qDebug() << "Found LV2 sub plugin key of type" << + // m_type << ":" << pr.first.c_str(); + } + } +} + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index c6ab433eac4..fe77ac49671 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -19,6 +19,7 @@ SET(LMMS_SRCS gui/LfoControllerDialog.cpp gui/LmmsPalette.cpp gui/LmmsStyle.cpp + gui/Lv2ViewBase.cpp gui/MainApplication.cpp gui/MainWindow.cpp gui/MidiSetupWidget.cpp diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 4311e4e053b..360d3c74fa6 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -405,7 +405,11 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) m_previewPlayHandle = s; delete tf; } - else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" ) && + else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" +#ifdef LMMS_HAVE_LV2 + || f->extension() == "lv2" +#endif + ) && ! pluginFactory->pluginSupportingExtension(f->extension()).info.isNull() ) { m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin ); @@ -984,6 +988,11 @@ void FileItem::determineFileType( void ) m_type = VstPluginFile; m_handling = LoadByPlugin; } + else if( ext == "lv2" ) + { + m_type = PresetFile; + m_handling = LoadByPlugin; + } else { m_type = UnknownFile; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp new file mode 100644 index 00000000000..190f0d349e4 --- /dev/null +++ b/src/gui/Lv2ViewBase.cpp @@ -0,0 +1,249 @@ +/* + * Lv2ViewBase.cpp - base class for Lv2 plugin views + * + * Copyright (c) 2018-2019 Johannes Lorenz + * + * 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 "Lv2ViewBase.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include +#include +#include + +#include "Controls.h" +#include "Engine.h" +#include "GuiApplication.h" +#include "embed.h" +#include "gui_templates.h" +#include "LedCheckbox.h" +#include "Lv2ControlBase.h" +#include "Lv2Manager.h" +#include "Lv2Proc.h" +#include "Lv2Ports.h" +#include "MainWindow.h" +#include "SubWindow.h" + + + + +Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, + int colNum, int curProc, int nProc, const QString& name) : + LinkedModelGroupViewBase (parent, ctrlBase->isLinking(), colNum, + curProc, nProc, name) +{ + class SetupWidget : public Lv2Ports::Visitor + { + AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); + public: + QWidget* par; // input + ControlBase* control = nullptr; // output + void visit(Lv2Ports::Control& port) override + { + if(port.m_flow == Lv2Ports::Flow::Input) + { + using PortVis = Lv2Ports::Vis; + + switch (port.m_vis) + { + case PortVis::None: + control = new KnobControl(par); + break; + case PortVis::Integer: + control = new LcdControl((port.m_max <= 9.0f) ? 1 : 2, + par); + break; + case PortVis::Enumeration: + control = new ComboControl(par); + break; + case PortVis::Toggled: + control = new CheckControl(par); + break; + } + control->setText(port.name()); + + LilvNodes* props = lilv_port_get_value( + port.m_plugin, port.m_port, commentUri.get()); + LILV_FOREACH(nodes, itr, props) + { + const LilvNode* nod = lilv_nodes_get(props, itr); + control->topWidget()->setToolTip(lilv_node_as_string(nod)); + break; + } + lilv_nodes_free(props); + } + } + }; + + for (Lv2Ports::PortBase* port : ctrlBase->getPorts()) + { + SetupWidget setup; + setup.par = this; + port->accept(setup); + + if(setup.control) + { + addControl(setup.control); + } + } +} + + + + +Lv2ViewProc::~Lv2ViewProc() {} + + + + +AutoLilvNode Lv2ViewProc::uri(const char *uriStr) +{ + return Engine::getLv2Manager()->uri(uriStr); +} + + + + +Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) + : LinkedModelGroupsViewBase (ctrlBase) +{ + QGridLayout* grid = new QGridLayout(meAsWidget); + + QHBoxLayout* btnBox = new QHBoxLayout(); + grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); + if(/* DISABLES CODE */ (false)) { + m_reloadPluginButton = new QPushButton(QObject::tr("Reload Plugin"), + meAsWidget); + btnBox->addWidget(m_reloadPluginButton, 0); + } + + if (/* DISABLES CODE */ (false)) // TODO: check if the plugin has the UI extension + { + m_toggleUIButton = new QPushButton(QObject::tr("Show GUI"), + meAsWidget); + m_toggleUIButton->setCheckable(true); + m_toggleUIButton->setChecked(false); + m_toggleUIButton->setIcon(embed::getIconPixmap("zoom")); + m_toggleUIButton->setFont( + pointSize<8>(m_toggleUIButton->font())); + m_toggleUIButton->setWhatsThis( + QObject::tr("Click here to show or hide the " + "Lv2 graphical user interface (GUI).")); + btnBox->addWidget(m_toggleUIButton, 0); + } + btnBox->addStretch(1); + + meAsWidget->setAcceptDrops(true); + + // note: the lifetime of C++ objects ends after the top expression in the + // expression syntax tree, so the AutoLilvNode gets freed after the function + // has been called + LilvNodes* props = lilv_plugin_get_value(ctrlBase->getPlugin(), + uri(LILV_NS_RDFS "comment").get()); + LILV_FOREACH(nodes, itr, props) + { + const LilvNode* node = lilv_nodes_get(props, itr); + QLabel* infoLabel = new QLabel(lilv_node_as_string(node)); + infoLabel->setWordWrap(true); + infoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + m_helpButton = new QPushButton(QObject::tr("Help")); + m_helpButton->setCheckable(true); + btnBox->addWidget(m_helpButton); + + m_helpWindow = gui->mainWindow()->addWindowedWidget(infoLabel); + m_helpWindow->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Expanding); + m_helpWindow->hide(); + + break; + } + lilv_nodes_free(props); + + int nProcs = static_cast(ctrlBase->controls().size()); + Q_ASSERT(m_colNum % nProcs == 0); + int colsEach = m_colNum / nProcs; + for (int i = 0; i < nProcs; ++i) + { + Lv2ViewProc* vpr = new Lv2ViewProc(meAsWidget, + ctrlBase->controls()[static_cast(i)], + colsEach, i, nProcs); + grid->addWidget(vpr, Rows::ProcRow, i); + m_procViews.push_back(vpr); + } + + LedCheckBox* led = globalLinkLed(); + if(led) + grid->addWidget(led, Rows::LinkChannelsRow, 0, 1, m_colNum); +} + + + + +Lv2ViewBase::~Lv2ViewBase() {} + + + + +void Lv2ViewBase::toggleHelp(bool visible) +{ + if( m_helpWindow ) + { + if( visible ) + { + m_helpWindow->show(); + m_helpWindow->raise(); + } + else + { + m_helpWindow->hide(); + } + } +} + + + + +void Lv2ViewBase::modelChanged(Lv2ControlBase *ctrlBase) +{ + // reconnect models + if(m_toggleUIButton) + { + m_toggleUIButton->setChecked(ctrlBase->hasGui()); + } + + LinkedModelGroupsViewBase::modelChanged(ctrlBase); +} + + + + +AutoLilvNode Lv2ViewBase::uri(const char *uriStr) +{ + return Engine::getLv2Manager()->uri(uriStr); +} + + +#endif // LMMS_HAVE_LV2 diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 424c23d26e6..a0dd66a345c 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -137,7 +137,7 @@ MainWindow::MainWindow() : sideBar->appendTab( new FileBrowser( confMgr->userPresetsDir() + "*" + confMgr->factoryPresetsDir(), - "*.xpf *.cs.xml *.xiz", + "*.xpf *.cs.xml *.xiz *.lv2", tr( "My Presets" ), embed::getIconPixmap( "preset_file" ).transformed( QTransform().rotate( 90 ) ), splitter , false, true ) ); diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 02d07f1e413..819eb809f28 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -12,6 +12,7 @@ #cmakedefine LMMS_HAVE_FLUIDSYNTH #cmakedefine LMMS_HAVE_JACK #cmakedefine LMMS_HAVE_WEAKJACK +#cmakedefine LMMS_HAVE_LV2 #cmakedefine LMMS_HAVE_MP3LAME #cmakedefine LMMS_HAVE_OGGVORBIS #cmakedefine LMMS_HAVE_OSS From 5dc7bf67c0f049bafde1853d34742056d428fa15 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 17 Mar 2019 12:01:11 +0100 Subject: [PATCH 005/120] Fix CI: assert -> Q_ASSERT --- src/core/lv2/Lv2ControlBase.cpp | 4 +++- src/core/lv2/Lv2SubPluginFeatures.cpp | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 74e1b1a96f0..2d6a6b3ebb3 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -26,6 +26,8 @@ #ifdef LMMS_HAVE_LV2 +#include + #include "AutomatableModel.h" #include "Engine.h" #include "Lv2Manager.h" @@ -282,7 +284,7 @@ void Lv2ControlBase::reloadPlugin() // refresh ports that are only read on restore m_ports.samplerate = Engine::mixer()->processingSampleRate(); int16_t fpp = Engine::mixer()->framesPerPeriod(); - assert(fpp >= 0); + Q_ASSERT(fpp >= 0); m_ports.buffersize = static_cast(fpp); if (m_lv2Plugin->restore_has()) diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index c5200ae002d..52cc93e65a1 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "AudioDevice.h" @@ -49,7 +50,7 @@ const LilvPlugin *Lv2SubPluginFeatures::getPlugin( { const LilvPlugin* result = Engine::getLv2Manager()-> getPlugin(k.attributes["uri"]); - assert(result); + Q_ASSERT(result); return result; } From ba70b60d2d8e06147559145a587e7314bed5ac6d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 17 Mar 2019 14:13:12 +0100 Subject: [PATCH 006/120] Fixed warning about uninitialized values --- src/core/lv2/Lv2Proc.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index a19d8cb2f8c..06d488acf98 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -436,8 +436,8 @@ void Lv2Proc::createPorts() { if(!audio.isSideChain()) { - StereoPortRef* portRef; StereoPortRef dummy; + StereoPortRef* portRef = &dummy; switch(audio.m_flow) { case Lv2Ports::Flow::Input: @@ -447,7 +447,6 @@ void Lv2Proc::createPorts() portRef = &proc->m_outPorts; break; case Lv2Ports::Flow::Unknown: - portRef = &dummy; break; } // in Lv2, leftPort is defined to be the first port From ae8473caa73516bcd8b6466e6568cc1ee73be65c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 17 Mar 2019 14:41:23 +0100 Subject: [PATCH 007/120] Remove a lot of dead code --- plugins/Lv2Effect/Lv2FxControlDialog.cpp | 31 +---- plugins/Lv2Instrument/Lv2Instrument.cpp | 121 ++------------------ src/core/lv2/Lv2ControlBase.cpp | 139 +---------------------- src/core/lv2/Lv2Manager.cpp | 3 - src/core/lv2/Lv2Proc.cpp | 34 +----- src/core/lv2/Lv2SubPluginFeatures.cpp | 16 +-- src/gui/Lv2ViewBase.cpp | 4 +- 7 files changed, 21 insertions(+), 327 deletions(-) diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index 04c7f79aff5..a9f1cf5f25a 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -66,24 +66,6 @@ Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : -/* -// TODO: common UI class..., as this must be usable for instruments, too -Lv2ControlDialog::~Lv2ControlDialog() -{ - Lv2Effect *model = castModel(); - - if (model && lv2Controls()->m_lv2Descriptor->ui_ext() && -lv2Controls()->m_hasGUI) - { - qDebug() << "shutting down UI..."; - model->m_plugin->ui_ext_show(false); - } -} -*/ - - - - void Lv2FxControlDialog::reloadPlugin() { lv2Controls()->reloadPlugin(); } @@ -91,18 +73,11 @@ void Lv2FxControlDialog::reloadPlugin() { lv2Controls()->reloadPlugin(); } void Lv2FxControlDialog::toggleUI() { -#if 0 - Lv2Effect *model = castModel(); - if (model->m_lv2Descriptor->ui_ext() && - model->m_hasGUI != m_toggleUIButton->isChecked()) - { - model->m_hasGUI = m_toggleUIButton->isChecked(); - model->m_plugin->ui_ext_show(model->m_hasGUI); - ControllerConnection::finalizeConnections(); - } -#endif } + + + void Lv2FxControlDialog::toggleHelp(bool visible) { Lv2ViewBase::toggleHelp(visible); diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 53e88362dfa..543ebcbcb42 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -144,57 +144,11 @@ void Lv2Instrument::loadFile(const QString &file) bool Lv2Instrument::handleMidiEvent( const MidiEvent &event, const MidiTime &time, f_cnt_t offset) { + // this function can be called from GUI threads while the plugin is running, + // so this requires caching, e.g. in ringbuffers (void)time; (void)offset; -#ifdef TODO - switch (event.type()) - { - // the old zynaddsubfx plugin always uses channel 0 - case MidiNoteOn: - if (event.velocity() > 0) - { - if (event.key() <= 0 || event.key() >= 128) - { - break; - } - if (m_runningNotes[event.key()] > 0) - { - m_pluginMutex.lock(); - writeOsc("/noteOff", "ii", 0, event.key()); - m_pluginMutex.unlock(); - } - ++m_runningNotes[event.key()]; - m_pluginMutex.lock(); - writeOsc("/noteOn", "iii", 0, event.key(), - event.velocity()); - m_pluginMutex.unlock(); - } - break; - case MidiNoteOff: - if (event.key() > 0 && event.key() < 128) { - if (--m_runningNotes[event.key()] <= 0) - { - m_pluginMutex.lock(); - writeOsc("/noteOff", "ii", 0, event.key()); - m_pluginMutex.unlock(); - } - } - break; - /* case MidiPitchBend: - m_master->SetController( event.channel(), - C_pitchwheel, event.pitchBend()-8192 ); break; case - MidiControlChange: m_master->SetController( event.channel(), - midiIn.getcontroller( - event.controllerNumber() ), event.controllerValue() ); - break;*/ - default: - break; - } -#else (void)event; -#endif - // those can be called from GUI threads while the plugin is running, - // so this requires caching, e.g. in ringbuffers return true; } #endif @@ -206,45 +160,6 @@ bool Lv2Instrument::handleMidiEvent( #ifndef LV2_INSTRUMENT_USE_MIDI void Lv2Instrument::playNote(NotePlayHandle *nph, sampleFrame *) { - // no idea what that means - if (nph->isMasterNote() || (nph->hasParent() && nph->isReleased())) - { - return; - } - - const f_cnt_t tfp = nph->totalFramesPlayed(); - - const float LOG440 = 2.643452676f; - - int midiNote = (int)floor( - 12.0 * (log2(nph->unpitchedFrequency()) - LOG440) - 4.0); - - qDebug() << "midiNote: " << midiNote << ", r? " << nph->isReleased(); - // out of range? - if (midiNote <= 0 || midiNote >= 128) - { - return; - } - - if (tfp == 0) - { - const int baseVelocity = - instrumentTrack()->midiPort()->baseVelocity(); - m_plugin->send_osc("/noteOn", "iii", 0, midiNote, baseVelocity); - } - else if (nph->isReleased() && - !nph->instrumentTrack() - ->isSustainPedalPressed()) // note is released during - // this period - { - m_plugin->send_osc("/noteOff", "ii", 0, midiNote); - } - else if (nph->framesLeft() <= 0) - { - m_plugin->send_osc("/noteOff", "ii", 0, midiNote); - } - // those can be called from GUI threads while the plugin is running, - // so this requires caching, e.g. in ringbuffers } #endif @@ -253,16 +168,14 @@ void Lv2Instrument::playNote(NotePlayHandle *nph, sampleFrame *) void Lv2Instrument::play(sampleFrame *buf) { - //if (m_plugin) - { - copyModelsFromLmms(); + copyModelsFromLmms(); - fpp_t fpp = Engine::mixer()->framesPerPeriod(); + fpp_t fpp = Engine::mixer()->framesPerPeriod(); - run(static_cast(fpp)); + run(static_cast(fpp)); + + copyBuffersToLmms(buf, fpp); - copyBuffersToLmms(buf, fpp); - } instrumentTrack()->processAudioBuffer( buf, Engine::mixer()->framesPerPeriod(), nullptr); } @@ -351,16 +264,7 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : -Lv2InsView::~Lv2InsView() -{ - Lv2Instrument *model = castModel(); - if (model && /* DISABLES CODE */ (false) - /* TODO: check if plugin has UI extension */ && model->hasGui()) - { - qDebug() << "shutting down UI..."; - // TODO: tell plugin to hide the UI - } -} +Lv2InsView::~Lv2InsView() {} @@ -411,15 +315,6 @@ void Lv2InsView::reloadPlugin() void Lv2InsView::toggleUI() { - Lv2Instrument *model = castModel(); - if (/* DISABLES CODE */ (false) - /* TODO: check if plugin has the UI extension */ && - model->hasGui() != m_toggleUIButton->isChecked()) - { - model->setHasGui(m_toggleUIButton->isChecked()); - // TODO: show the UI - ControllerConnection::finalizeConnections(); - } } diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 2d6a6b3ebb3..c112962766d 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -149,46 +149,9 @@ void Lv2ControlBase::run(unsigned frames) { void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) { -#ifdef TODO - // save internal data? - if (m_lv2Plugin->save_has()) - { - QTemporaryFile tf; - if (tf.open()) - { - const std::string fn = QSTR_TO_STDSTR( - QDir::toNativeSeparators(tf.fileName())); - m_plugin->save(fn.c_str(), ++m_saveTicket); - - while (!m_plugin->save_check(fn.c_str(), m_saveTicket)) { - QThread::msleep(1); - } - - QDomCDATASection cdata = doc.createCDATASection( - QString::fromUtf8(tf.readAll())); - that.appendChild(cdata); - } - tf.remove(); - } - - // save connected models - if (m_connectedModels.size()) - { - QDomElement newNode = doc.createElement("connected-models"); - QMap::const_iterator i = - m_connectedModels.constBegin(); - while (i != m_connectedModels.constEnd()) - { - i.value()->saveSettings(doc, newNode, i.key()); - ++i; - } - - that.appendChild(newNode); - } -#else + // TODO (void)doc; (void)that; -#endif } @@ -196,69 +159,7 @@ void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) void Lv2ControlBase::loadSettings(const QDomElement &that) { -#ifdef TODO - if (!that.hasChildNodes()) - { - return; - } - - for (QDomNode node = that.firstChild(); !node.isNull(); - node = node.nextSibling()) - { - QDomCDATASection cdata = node.toCDATASection(); - QDomElement elem; - // load internal state? - if (!cdata.isNull() && m_lv2Plugin->load_has()) - { - QTemporaryFile tf; - tf.setAutoRemove(false); - if (tf.open()) - { - tf.write(cdata.data().toUtf8()); - tf.flush(); - loadFileInternal(tf.fileName()); - } - } - // load connected models? - else if (node.nodeName() == "connected-models" && - !(elem = node.toElement()).isNull()) - { - QDomNamedNodeMap attrs = elem.attributes(); - - auto do_load = [&](const QString &name, - QDomElement elem) { - AutomatableModel *m = modelAtPort(name); - // this will automatically - // load any "connection" node: - m->loadSettings(elem, name); - m_connectedModels[name] = m; - }; - - for (int i = 0; i < attrs.count(); ++i) - { - QDomAttr attribute = attrs.item(i).toAttr(); - do_load(attribute.name(), elem); - } - - for (QDomElement portnode = elem.firstChildElement(); - !portnode.isNull(); - portnode = portnode.nextSiblingElement()) - { - if (portnode.nodeName() != "connection") - { - QString name = portnode.nodeName(); - if (name == "automatablemodel") { - name = portnode.attribute( - "nodename"); - } - do_load(name, elem); - } - } - } - } -#else (void)that; -#endif } @@ -266,13 +167,7 @@ void Lv2ControlBase::loadSettings(const QDomElement &that) void Lv2ControlBase::loadFile(const QString &file) { -#ifdef TODO - loadFileInternal(file); - setNameFromFile(QFileInfo(file).baseName().replace( - QRegExp("^[0-9]{4}-"), QString())); -#else (void)file; -#endif } @@ -280,37 +175,7 @@ void Lv2ControlBase::loadFile(const QString &file) void Lv2ControlBase::reloadPlugin() { -#ifdef TODO - // refresh ports that are only read on restore - m_ports.samplerate = Engine::mixer()->processingSampleRate(); - int16_t fpp = Engine::mixer()->framesPerPeriod(); - Q_ASSERT(fpp >= 0); - m_ports.buffersize = static_cast(fpp); - - if (m_lv2Plugin->restore_has()) - { - // use the offered restore function - m_plugin->restore(++m_restoreTicket); - - while (!m_plugin->restore_check(m_restoreTicket)) { - QThread::msleep(1); - } - } - else - { - // save state of current plugin instance - DataFile m(spe()); - - saveSettings(m, m.content()); - - shutdownPlugin(); - // init plugin (will create a new instance) - initPlugin(); - - // and load the settings again - loadSettings(m.content()); - } -#endif + // TODO } diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index fb3266ce3d4..4f3dfe8e4f2 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -103,9 +103,6 @@ bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) const LilvPluginClass* gen = lilv_plugin_classes_get_by_uri(allClasses, uri(uriStr).get()); - // lv2:Generator is what can be generating an LMMS instrument track - // lv2:Instrument is lv:Generator with MIDI/piano input - // => LMMS "Instrument" corresponds to lv2:Generator auto clssEq = [](const LilvPluginClass* pc1, const LilvPluginClass* pc2) -> bool { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 06d488acf98..32e17588eb0 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -236,31 +236,7 @@ AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) } else { -#if 0 - AutomatableModel *lv2Mod; - { - Lv2OscModelFactory vis(this, url.path()); - lv2::port_ref_base &base = - m_plugin->port(url.path().toUtf8().data()); - base.accept(vis); - lv2Mod = vis.m_res; - } - - if (lv2Mod) - { - m_connectedModels.insert(url.path(), lv2Mod); - mod = lv2Mod; - } - else - { -#endif - qDebug() << "LMMS: Could not create model from " - << "OSC port (received port\"" << uri - << "\", path \"" << uri << "\")"; - mod = nullptr; -#if 0 - } -#endif + mod = nullptr; } return mod; } @@ -304,15 +280,7 @@ void Lv2Proc::shutdownPlugin() void Lv2Proc::loadFileInternal(const QString &file) { -#ifdef TODO - const QByteArray fn = file.toUtf8(); - m_plugin->load(fn.data(), ++m_saveTicket); - while (!m_plugin->load_check(fn.data(), m_saveTicket)) { - QThread::msleep(1); - } -#else (void)file; -#endif } diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 52cc93e65a1..3960c1e67d3 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -27,6 +27,8 @@ #include "Lv2SubPluginFeatures.h" +#ifdef LMMS_HAVE_LV2 + #include #include #include @@ -114,18 +116,6 @@ void Lv2SubPluginFeatures::fillDescriptionWidget( const LilvNodes* extensions = lilv_plugin_get_extension_data(plug); (void)extensions; -/* QLabel *requiresRealTime = new QLabel(_parent); - requiresRealTime->setText(QWidget::tr("Requires Real Time: ") + - ( - - ->properties.realtime_dependency ? QWidget::tr("Yes") - : QWidget::tr("No"))); - - QLabel *realTimeCapable = new QLabel(_parent); - realTimeCapable->setText(QWidget::tr("Real Time Capable: ") + - (plug->properties.hard_rt_capable ? QWidget::tr("Yes") - : QWidget::tr("No")));*/ - // possibly TODO: version, project, plugin type, number of channels } @@ -198,3 +188,5 @@ void Lv2SubPluginFeatures::listSubPluginKeys( } } +#endif // LMMS_HAVE_LV2 + diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 190f0d349e4..f245da7b57b 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -202,7 +202,9 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) -Lv2ViewBase::~Lv2ViewBase() {} +Lv2ViewBase::~Lv2ViewBase() { + // TODO: hide UI if required +} From 2a28dedae950d8126d630338b11370e6cf8b08f2 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Mar 2019 23:04:55 +0100 Subject: [PATCH 008/120] VST Bugfix, stolen from master --- include/Plugin.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/Plugin.h b/include/Plugin.h index 31c83c4ba93..4715d1d835c 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -207,9 +207,7 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject virtual QString displayName(const Key& k) const { - return k.isValid() - ? k.desc->displayName - : k.name; + return k.isValid() ? k.name : QString(); } virtual QString description(const Key& k) const From 7961b2560a381f19c1ff13210d396885031b90cd Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 4 Apr 2019 18:33:50 +0200 Subject: [PATCH 009/120] Apply suggestions from code review Apply fixes according to coding conventions, thanks to @Spekular Co-Authored-By: JohannesLorenz <1042576+JohannesLorenz@users.noreply.github.com> --- plugins/Lv2Effect/Lv2Effect.cpp | 4 ++-- plugins/Lv2Effect/Lv2FxControlDialog.cpp | 6 +++--- plugins/Lv2Instrument/Lv2Instrument.cpp | 12 ++++++------ src/core/lv2/Lv2ControlBase.cpp | 20 ++++++++++---------- src/core/lv2/Lv2Ports.cpp | 12 +++++++----- src/gui/FileBrowser.cpp | 4 ++-- src/gui/Lv2ViewBase.cpp | 13 +++++++------ 7 files changed, 37 insertions(+), 34 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 33c8a19a1ae..4a9489f665d 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -71,7 +71,7 @@ Lv2Effect::~Lv2Effect() bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { - if(!isEnabled() || !isRunning()) + if (!isEnabled() || !isRunning()) { return false; } @@ -102,7 +102,7 @@ PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) { using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; Lv2Effect* eff = new Lv2Effect(_parent, static_cast(_data)); - if(!eff->isValid()) + if (!eff->isValid()) eff = nullptr; return eff; } diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index a9f1cf5f25a..ccebd5fca08 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -46,15 +46,15 @@ Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : EffectControlDialog(controls), Lv2ViewBase(this, controls) { - if(m_reloadPluginButton) { + if (m_reloadPluginButton) { connect(m_reloadPluginButton, SIGNAL(toggled(bool)), this, SLOT(reloadPlugin())); } - if(m_toggleUIButton) { + if (m_toggleUIButton) { connect(m_toggleUIButton, SIGNAL(toggled(bool)), this, SLOT(toggleUI())); } - if(m_helpButton) { + if (m_helpButton) { connect(m_helpButton, SIGNAL(toggled(bool)), this, SLOT(toggleHelp(bool))); } diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 543ebcbcb42..db201ea3d23 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -73,7 +73,7 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, Instrument(instrumentTrackArg, &lv2instrument_plugin_descriptor, key), Lv2ControlBase(this, key->attributes["uri"]) { - if(Lv2ControlBase::isValid()) + if (Lv2ControlBase::isValid()) { #ifdef LV2_INSTRUMENT_USE_MIDI for (int i = 0; i < NumKeys; ++i) { @@ -84,7 +84,7 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, this, SLOT(updatePitchRange())); connect(Engine::mixer(), SIGNAL(sampleRateChanged()), this, SLOT(reloadPlugin())); - if(multiChannelLinkModel()) { + if (multiChannelLinkModel()) { connect(multiChannelLinkModel(), SIGNAL(dataChanged()), this, SLOT(updateLinkStatesFromGlobal())); connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), @@ -247,15 +247,15 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : Lv2ViewBase(this, _instrument) { setAutoFillBackground(true); - if(m_reloadPluginButton) { + if (m_reloadPluginButton) { connect(m_reloadPluginButton, SIGNAL(toggled(bool)), this, SLOT(reloadPlugin())); } - if(m_toggleUIButton) { + if (m_toggleUIButton) { connect(m_toggleUIButton, SIGNAL(toggled(bool)), this, SLOT(toggleUI())); } - if(m_helpButton) { + if (m_helpButton) { connect(m_helpButton, SIGNAL(toggled(bool)), this, SLOT(toggleHelp(bool))); } @@ -346,7 +346,7 @@ PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) Lv2Instrument* ins = new Lv2Instrument( static_cast(_parent), static_cast(_data )); - if(!ins->isValid()) + if (!ins->isValid()) ins = nullptr; return ins; } diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index c112962766d..8b47a00d17e 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -49,13 +49,13 @@ Plugin::PluginTypes Lv2ControlBase::check(const LilvPlugin *plugin, Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : m_plugin(Engine::getLv2Manager()->getPlugin(uri)) { - if(m_plugin) + if (m_plugin) { int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo while (channelsLeft > 0) { Lv2Proc* newOne = new Lv2Proc(m_plugin, that); - if(newOne->isValid()) + if (newOne->isValid()) { channelsLeft -= std::max( 1 + static_cast(newOne->inPorts().m_right), @@ -70,10 +70,10 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : channelsLeft = 0; } } - if(m_valid) + if (m_valid) { m_channelsPerProc = DEFAULT_CHANNELS / m_procs.size(); - if(m_procs.size() > 1) + if (m_procs.size() > 1) { m_procs[0]->makeLinkingProc(); createMultiChannelLinkModel(); @@ -97,7 +97,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : Lv2ControlBase::~Lv2ControlBase() { - for(Lv2Proc* c : m_procs) { delete c; } + for (Lv2Proc* c : m_procs) { delete c; } } @@ -112,7 +112,7 @@ LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) void Lv2ControlBase::copyModelsFromLmms() { - for(Lv2Proc* c : m_procs) { c->copyModelsFromCore(); } + for (Lv2Proc* c : m_procs) { c->copyModelsFromCore(); } } @@ -120,7 +120,7 @@ void Lv2ControlBase::copyModelsFromLmms() { void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { unsigned offset = 0; - for(Lv2Proc* c : m_procs) { + for (Lv2Proc* c : m_procs) { c->copyBuffersFromCore(buf, offset, m_channelsPerProc, frames); offset += m_channelsPerProc; } @@ -131,7 +131,7 @@ void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { unsigned offset = 0; - for(const Lv2Proc* c : m_procs) { + for (const Lv2Proc* c : m_procs) { c->copyBuffersToCore(buf, offset, m_channelsPerProc, frames); offset += m_channelsPerProc; } @@ -141,7 +141,7 @@ void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { void Lv2ControlBase::run(unsigned frames) { - for(Lv2Proc* c : m_procs) { c->run(frames); } + for (Lv2Proc* c : m_procs) { c->run(frames); } } @@ -183,7 +183,7 @@ void Lv2ControlBase::reloadPlugin() std::size_t Lv2ControlBase::controlCount() const { std::size_t res = 0; - for(const Lv2Proc* c : m_procs) + for (const Lv2Proc* c : m_procs) res += c->controlCount(); return res; } diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 9a285ffbf97..df93b60a67e 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -131,10 +131,11 @@ std::vector Meta::get(const LilvPlugin *plugin, m_def = .0f; m_min = .0f; m_max = .0f; - if (isA(LV2_CORE__ControlPort)) { + if (isA(LV2_CORE__ControlPort)) + { m_type = Type::Control; - if(m_flow == Flow::Input) + if (m_flow == Flow::Input) { bool isToggle = m_vis == Vis::Toggled; @@ -145,7 +146,8 @@ std::vector Meta::get(const LilvPlugin *plugin, auto takeRangeValue = [&](LilvNode* node, float& storeHere, PluginIssueType it) { - if(node) { + if(node) + { storeHere = lilv_node_as_float(node); } else { @@ -155,12 +157,12 @@ std::vector Meta::get(const LilvPlugin *plugin, }; takeRangeValue(defN, m_def, portHasNoDef); - if(!isToggle) + if (!isToggle) { takeRangeValue(minN, m_min, portHasNoMin); takeRangeValue(maxN, m_max, portHasNoMax); - if(m_max - m_min > 15.0f) + if (m_max - m_min > 15.0f) { // range too large for spinbox visualisation, use knobs // e.g. 0...15 would be OK diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 360d3c74fa6..38e90d7b6ea 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -405,7 +405,7 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) m_previewPlayHandle = s; delete tf; } - else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" + else if ( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" #ifdef LMMS_HAVE_LV2 || f->extension() == "lv2" #endif @@ -988,7 +988,7 @@ void FileItem::determineFileType( void ) m_type = VstPluginFile; m_handling = LoadByPlugin; } - else if( ext == "lv2" ) + else if ( ext == "lv2" ) { m_type = PresetFile; m_handling = LoadByPlugin; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index f245da7b57b..92a5135c60f 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -103,7 +103,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, setup.par = this; port->accept(setup); - if(setup.control) + if (setup.control) { addControl(setup.control); } @@ -133,7 +133,8 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) QHBoxLayout* btnBox = new QHBoxLayout(); grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); - if(/* DISABLES CODE */ (false)) { + if(/* DISABLES CODE */ (false)) + { m_reloadPluginButton = new QPushButton(QObject::tr("Reload Plugin"), meAsWidget); btnBox->addWidget(m_reloadPluginButton, 0); @@ -195,7 +196,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) } LedCheckBox* led = globalLinkLed(); - if(led) + if (led) grid->addWidget(led, Rows::LinkChannelsRow, 0, 1, m_colNum); } @@ -211,9 +212,9 @@ Lv2ViewBase::~Lv2ViewBase() { void Lv2ViewBase::toggleHelp(bool visible) { - if( m_helpWindow ) + if ( m_helpWindow ) { - if( visible ) + if ( visible ) { m_helpWindow->show(); m_helpWindow->raise(); @@ -231,7 +232,7 @@ void Lv2ViewBase::toggleHelp(bool visible) void Lv2ViewBase::modelChanged(Lv2ControlBase *ctrlBase) { // reconnect models - if(m_toggleUIButton) + if (m_toggleUIButton) { m_toggleUIButton->setChecked(ctrlBase->hasGui()); } From 71e0951e85f6dd7bb1caebd07a7007cf3e4b45a8 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 5 Apr 2019 19:54:44 +0200 Subject: [PATCH 010/120] Whitespace fixes --- include/Lv2Basics.h | 7 ++---- include/Lv2Manager.h | 16 +++---------- plugins/Lv2Effect/Lv2Effect.cpp | 9 ++------ plugins/Lv2Instrument/Lv2Instrument.cpp | 7 ++---- src/core/lv2/Lv2ControlBase.cpp | 3 +-- src/core/lv2/Lv2Ports.cpp | 30 +++++++++---------------- src/core/lv2/Lv2Proc.cpp | 22 +++++------------- src/gui/Lv2ViewBase.cpp | 16 +++---------- 8 files changed, 28 insertions(+), 82 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index e6ff63d6ed9..8257cc468c1 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -40,11 +40,8 @@ struct AutoLilvNode LilvNode* n; AutoLilvNode(LilvNode* n) : n(n) {} AutoLilvNode(const AutoLilvNode& other) = delete; - AutoLilvNode(AutoLilvNode&& other) { - n = other.n; - other.n = nullptr; - } - ~AutoLilvNode() { if(n) lilv_node_free(n); } + AutoLilvNode(AutoLilvNode&& other) { n = other.n; other.n = nullptr; } + ~AutoLilvNode() { if(n) { lilv_node_free(n); } } const LilvNode* get() const { return n; } }; diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index cf5aad572de..de6f3367de5 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -128,19 +128,9 @@ class Lv2Manager { std::map::iterator m_itr; public: - bool operator!=(const Iterator &other) - { - return m_itr != other.m_itr; - } - Iterator &operator++() - { - ++m_itr; - return *this; - } - std::pair &operator*() - { - return *m_itr; - } + bool operator!=(const Iterator &other) { return m_itr != other.m_itr; } + Iterator &operator++() { ++m_itr; return *this; } + std::pair &operator*() { return *m_itr; } Iterator(std::map::iterator itr) : m_itr(itr) {} }; diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 4a9489f665d..0090733054a 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -71,15 +71,11 @@ Lv2Effect::~Lv2Effect() bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { - if (!isEnabled() || !isRunning()) - { - return false; - } + if (!isEnabled() || !isRunning()) { return false; } Lv2FxControls& ctrl = m_controls; ctrl.copyBuffersFromLmms(buf, frames); - m_controls.copyModelsFromLmms(); // m_pluginMutex.lock(); @@ -102,8 +98,7 @@ PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) { using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; Lv2Effect* eff = new Lv2Effect(_parent, static_cast(_data)); - if (!eff->isValid()) - eff = nullptr; + if (!eff->isValid()) { eff = nullptr; } return eff; } diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index db201ea3d23..f818d080e6c 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -76,9 +76,7 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, if (Lv2ControlBase::isValid()) { #ifdef LV2_INSTRUMENT_USE_MIDI - for (int i = 0; i < NumKeys; ++i) { - m_runningNotes[i] = 0; - } + for (int i = 0; i < NumKeys; ++i) { m_runningNotes[i] = 0; } #endif connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()), this, SLOT(updatePitchRange())); @@ -346,8 +344,7 @@ PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) Lv2Instrument* ins = new Lv2Instrument( static_cast(_parent), static_cast(_data )); - if (!ins->isValid()) - ins = nullptr; + if (!ins->isValid()) { ins = nullptr; } return ins; } diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 8b47a00d17e..3b08c1f9c48 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -183,8 +183,7 @@ void Lv2ControlBase::reloadPlugin() std::size_t Lv2ControlBase::controlCount() const { std::size_t res = 0; - for (const Lv2Proc* c : m_procs) - res += c->controlCount(); + for (const Lv2Proc* c : m_procs) { res += c->controlCount(); } return res; } diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index df93b60a67e..18f70582c7a 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -119,12 +119,9 @@ std::vector Meta::get(const LilvPlugin *plugin, ? Vis::Toggled : Vis::None; - if (isA(LV2_CORE__InputPort)) { - m_flow = Flow::Input; - } - else if (isA(LV2_CORE__OutputPort)) { - m_flow = Flow::Output; - } else { + if (isA(LV2_CORE__InputPort)) { m_flow = Flow::Input; } + else if (isA(LV2_CORE__OutputPort)) { m_flow = Flow::Output; } + else { m_flow = Flow::Unknown; issue(unknownPortFlow, portName); } @@ -145,14 +142,10 @@ std::vector Meta::get(const LilvPlugin *plugin, isToggle ? nullptr : &maxN); auto takeRangeValue = [&](LilvNode* node, - float& storeHere, PluginIssueType it) { - if(node) - { - storeHere = lilv_node_as_float(node); - } - else { - issue(it, portName); - } + float& storeHere, PluginIssueType it) + { + if(node) { storeHere = lilv_node_as_float(node); } + else { issue(it, portName); } lilv_node_free(node); }; @@ -171,15 +164,12 @@ std::vector Meta::get(const LilvPlugin *plugin, } } } - else if (isA(LV2_CORE__AudioPort)) { - m_type = Type::Audio; - } else if (isA(LV2_CORE__CVPort)) { + else if (isA(LV2_CORE__AudioPort)) { m_type = Type::Audio; } + else if (isA(LV2_CORE__CVPort)) { issue(badPortType, "cvPort"); m_type = Type::Cv; } else { - if (m_optional) { - m_used = false; - } + if (m_optional) { m_used = false; } else { issue(PluginIssueType::unknownPortType, portName); m_type = Type::Unknown; diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 32e17588eb0..9f4e25fee5e 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -168,10 +168,7 @@ void Lv2Proc::copyModelsFromCore() } } copy; - for (Lv2Ports::PortBase* port : m_ports) - { - port->accept(copy); - } + for (Lv2Ports::PortBase* port : m_ports) { port->accept(copy); } } @@ -230,14 +227,8 @@ AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) // unused currently AutomatableModel *mod; auto itr = m_connectedModels.find(uri.toUtf8().data()); - if (itr != m_connectedModels.end()) - { - mod = itr->second; - } - else - { - mod = nullptr; - } + if (itr != m_connectedModels.end()) { mod = itr->second; } + else { mod = nullptr; } return mod; } @@ -418,11 +409,8 @@ void Lv2Proc::createPorts() break; } // in Lv2, leftPort is defined to be the first port - if(!portRef->m_left) { - portRef->m_left = &audio; - } else if(!portRef->m_right) { - portRef->m_right = &audio; - } + if(!portRef->m_left) { portRef->m_left = &audio; } + else if(!portRef->m_right) { portRef->m_right = &audio; } } } }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 92a5135c60f..30080186ec6 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -103,10 +103,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, setup.par = this; port->accept(setup); - if (setup.control) - { - addControl(setup.control); - } + if (setup.control) { addControl(setup.control); } } } @@ -214,15 +211,8 @@ void Lv2ViewBase::toggleHelp(bool visible) { if ( m_helpWindow ) { - if ( visible ) - { - m_helpWindow->show(); - m_helpWindow->raise(); - } - else - { - m_helpWindow->hide(); - } + if ( visible ) { m_helpWindow->show(); m_helpWindow->raise(); } + else { m_helpWindow->hide(); } } } From ce197d6e21d4b6263a651ae27e3dc22423e4e292 Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 5 Apr 2019 20:02:18 +0200 Subject: [PATCH 011/120] Apply suggestions from code review Fix whitespace after `if` and `else`. Co-Authored-By: JohannesLorenz <1042576+JohannesLorenz@users.noreply.github.com> --- src/core/lv2/Lv2Proc.cpp | 48 ++++++++++++++++++++-------------------- src/gui/Lv2ViewBase.cpp | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 9f4e25fee5e..4c2eea2c844 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -45,41 +45,41 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, enum { checkIn, checkOut, checkMax }; unsigned audioChannels[checkMax] = { 0, 0 }; // input and output count - for(unsigned portNum = 0; portNum < maxPorts; ++portNum) + for (unsigned portNum = 0; portNum < maxPorts; ++portNum) { Lv2Ports::Meta meta; // does all port checks: std::vector tmp = meta.get(plugin, portNum); std::move(tmp.begin(), tmp.end(), std::back_inserter(issues)); - if(meta.m_type == Lv2Ports::Type::Audio && + if (meta.m_type == Lv2Ports::Type::Audio && !portIsSideChain(plugin, lilv_plugin_get_port_by_index(plugin, portNum))) ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output]; } - if(audioChannels[checkIn] > 2) + if (audioChannels[checkIn] > 2) issues.emplace_back(tooManyInputChannels, std::to_string(audioChannels[0])); - if(audioChannels[checkOut] == 0) + if (audioChannels[checkOut] == 0) issues.emplace_back(noOutputChannel); - else if(audioChannels[checkOut] > 2) + else if (audioChannels[checkOut] > 2) issues.emplace_back(tooManyOutputChannels, std::to_string(audioChannels[1])); const LilvNodes* reqFeats = lilv_plugin_get_required_features(plugin); - LILV_FOREACH(nodes, itr, reqFeats) + LILV_FOREACH (nodes, itr, reqFeats) { issues.emplace_back(featureNotSupported, lilv_node_as_string(lilv_nodes_get(reqFeats, itr))); } - if(printIssues && issues.size()) + if (printIssues && issues.size()) { qDebug() << "Lv2 plugin " << lilv_node_as_string(lilv_plugin_get_name(plugin)) << " can not be loaded:"; - for(const PluginIssue& iss : issues) { qDebug() << " - " << iss; } + for (const PluginIssue& iss : issues) { qDebug() << " - " << iss; } } return (audioChannels[1] > 2 || audioChannels[0] > 2) @@ -96,7 +96,7 @@ Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : LinkedModelGroup(parent), m_plugin(plugin) { - if(m_plugin) + if (m_plugin) { initPlugin(); } @@ -118,7 +118,7 @@ Lv2Proc::~Lv2Proc() { shutdownPlugin(); } void Lv2Proc::dumpPorts() { std::size_t num = 0; - for(const Lv2Ports::PortBase* port: m_ports) + for (const Lv2Ports::PortBase* port: m_ports) { (void)port; dumpPort(num++); @@ -147,7 +147,7 @@ void Lv2Proc::copyModelsFromCore() { void visit(Lv2Ports::Control& ctrl) override { - if(ctrl.m_flow == Lv2Ports::Flow::Input) + if (ctrl.m_flow == Lv2Ports::Flow::Input) { FloatFromModel ffm; ffm.m_scalePointMap = &ctrl.m_scalePointMap; @@ -157,7 +157,7 @@ void Lv2Proc::copyModelsFromCore() } void visit(Lv2Ports::Cv& cv) override { - if(cv.m_flow == Lv2Ports::Flow::Input) + if (cv.m_flow == Lv2Ports::Flow::Input) { FloatFromModel ffm; ffm.m_scalePointMap = &cv.m_scalePointMap; @@ -179,13 +179,13 @@ void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, fpp_t frames) { inPorts().m_left->copyBuffersFromCore(buf, offset, frames); - if(num > 1) + if (num > 1) { // if the caller requests to take input from two channels, but we only // have one input channel... take medium of left and right for // mono input // (this happens if we have two outputs and only one input) - if(inPorts().m_right) + if (inPorts().m_right) inPorts().m_right->copyBuffersFromCore(buf, offset + 1, frames); else inPorts().m_left->addBuffersFromCore(buf, offset + 1, frames); @@ -200,7 +200,7 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf, fpp_t frames) const { outPorts().m_left->copyBuffersToCore(buf, offset + 0, frames); - if(num > 1) + if (num > 1) { // if the caller requests to copy into two channels, but we only have // one output channel, duplicate our output @@ -243,7 +243,7 @@ void Lv2Proc::initPlugin() Engine::mixer()->processingSampleRate(), nullptr); - if(m_instance) + if (m_instance) { for(unsigned portNum = 0; portNum < m_ports.size(); ++portNum) connectPort(portNum); @@ -290,12 +290,12 @@ void Lv2Proc::createPort(unsigned portNum) if (meta.m_type == Lv2Ports::Type::Control) { Lv2Ports::Control* ctrl = new Lv2Ports::Control; - if(meta.m_flow == Lv2Ports::Flow::Input) + if (meta.m_flow == Lv2Ports::Flow::Input) { LilvNode* node = lilv_port_get_name(m_plugin, lilvPort); QString dispName = lilv_node_as_string(node); lilv_node_free(node); - switch(meta.m_vis) + switch (meta.m_vis) { case Lv2Ports::Vis::None: { @@ -393,11 +393,11 @@ void Lv2Proc::createPorts() void visit(Lv2Ports::Audio& audio) override { - if(!audio.isSideChain()) + if (!audio.isSideChain()) { StereoPortRef dummy; StereoPortRef* portRef = &dummy; - switch(audio.m_flow) + switch (audio.m_flow) { case Lv2Ports::Flow::Input: portRef = &proc->m_inPorts; @@ -418,7 +418,7 @@ void Lv2Proc::createPorts() unsigned maxPorts = lilv_plugin_get_num_ports(m_plugin); m_ports.resize(maxPorts); - for(unsigned portNum = 0; portNum < maxPorts; ++portNum) + for (unsigned portNum = 0; portNum < maxPorts; ++portNum) { createPort(portNum); RegisterPort registerPort; @@ -465,7 +465,7 @@ void Lv2Proc::connectPort(unsigned num) void Lv2Proc::destroyPorts() { - for(Lv2Ports::PortBase* p: m_ports) { delete p; } + for (Lv2Ports::PortBase* p: m_ports) { delete p; } } @@ -478,7 +478,7 @@ void Lv2Proc::dumpPort(std::size_t num) void visit(const Lv2Ports::Control& ctrl) override { qDebug() << " control port"; // output ports may be uninitialized yet, only print inputs - if(ctrl.m_flow == Lv2Ports::Flow::Input) + if (ctrl.m_flow == Lv2Ports::Flow::Input) { qDebug() << " value:" << ctrl.m_val; } @@ -497,7 +497,7 @@ void Lv2Proc::dumpPort(std::size_t num) qDebug() << " flow: " << Lv2Ports::toStr(port.m_flow); qDebug() << " type: " << Lv2Ports::toStr(port.m_type); qDebug() << " visualization: " << Lv2Ports::toStr(port.m_vis); - if(port.m_type == Lv2Ports::Type::Control || port.m_type == Lv2Ports::Type::Cv) + if (port.m_type == Lv2Ports::Type::Control || port.m_type == Lv2Ports::Type::Cv) { qDebug() << " default:" << port.m_def; qDebug() << " min:" << port.m_min; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 30080186ec6..421f3bc5754 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -62,7 +62,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, ControlBase* control = nullptr; // output void visit(Lv2Ports::Control& port) override { - if(port.m_flow == Lv2Ports::Flow::Input) + if (port.m_flow == Lv2Ports::Flow::Input) { using PortVis = Lv2Ports::Vis; From f73d2ce178c1de18647be938ba16f1660189ffc5 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 5 Apr 2019 20:36:54 +0200 Subject: [PATCH 012/120] Fix more whitespace isssues --- include/Lv2Basics.h | 2 +- src/core/lv2/Lv2ControlBase.cpp | 2 +- src/core/lv2/Lv2Ports.cpp | 2 +- src/core/lv2/Lv2Proc.cpp | 8 ++++---- src/gui/Lv2ViewBase.cpp | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 8257cc468c1..c49fa9261f9 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -41,7 +41,7 @@ struct AutoLilvNode AutoLilvNode(LilvNode* n) : n(n) {} AutoLilvNode(const AutoLilvNode& other) = delete; AutoLilvNode(AutoLilvNode&& other) { n = other.n; other.n = nullptr; } - ~AutoLilvNode() { if(n) { lilv_node_free(n); } } + ~AutoLilvNode() { if (n) { lilv_node_free(n); } } const LilvNode* get() const { return n; } }; diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 3b08c1f9c48..1e564cea5b3 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -80,7 +80,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : } // initially link all controls - for(int i = 0; i < static_cast(m_procs[0]->controlCount()); + for (int i = 0; i < static_cast(m_procs[0]->controlCount()); ++i) { linkPort(i, true); } diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 18f70582c7a..4ffddb7cb8e 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -144,7 +144,7 @@ std::vector Meta::get(const LilvPlugin *plugin, auto takeRangeValue = [&](LilvNode* node, float& storeHere, PluginIssueType it) { - if(node) { storeHere = lilv_node_as_float(node); } + if (node) { storeHere = lilv_node_as_float(node); } else { issue(it, portName); } lilv_node_free(node); }; diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 4c2eea2c844..f8386499112 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -245,7 +245,7 @@ void Lv2Proc::initPlugin() if (m_instance) { - for(unsigned portNum = 0; portNum < m_ports.size(); ++portNum) + for (unsigned portNum = 0; portNum < m_ports.size(); ++portNum) connectPort(portNum); lilv_instance_activate(m_instance); } @@ -378,7 +378,7 @@ void Lv2Proc::createPorts() void visit(Lv2Ports::Control& ctrl) override { - if(ctrl.m_flow == Lv2Ports::Flow::Input) + if (ctrl.m_flow == Lv2Ports::Flow::Input) { AutomatableModel* amo = ctrl.m_connectedModel.get(); proc->m_connectedModels.emplace( @@ -409,8 +409,8 @@ void Lv2Proc::createPorts() break; } // in Lv2, leftPort is defined to be the first port - if(!portRef->m_left) { portRef->m_left = &audio; } - else if(!portRef->m_right) { portRef->m_right = &audio; } + if (!portRef->m_left) { portRef->m_left = &audio; } + else if (!portRef->m_right) { portRef->m_right = &audio; } } } }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 421f3bc5754..c03732fc0c8 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -130,7 +130,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) QHBoxLayout* btnBox = new QHBoxLayout(); grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); - if(/* DISABLES CODE */ (false)) + if (/* DISABLES CODE */ (false)) { m_reloadPluginButton = new QPushButton(QObject::tr("Reload Plugin"), meAsWidget); From 2610fae15bc4d9106b9ec780cdfef7e7f09f604a Mon Sep 17 00:00:00 2001 From: CYBERDEViLNL Date: Fri, 5 Apr 2019 23:06:19 +0200 Subject: [PATCH 013/120] PluginBrowser: Tree layout and search bar. --- include/PluginBrowser.h | 25 ++++---- src/gui/PluginBrowser.cpp | 121 ++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/include/PluginBrowser.h b/include/PluginBrowser.h index f7c46db7293..3cc54c6e47a 100644 --- a/include/PluginBrowser.h +++ b/include/PluginBrowser.h @@ -31,6 +31,10 @@ #include "SideBarWidget.h" #include "Plugin.h" +class QLineEdit; +class QTreeWidget; +class QTreeWidgetItem; + class PluginBrowser : public SideBarWidget { @@ -39,18 +43,18 @@ class PluginBrowser : public SideBarWidget PluginBrowser( QWidget * _parent ); virtual ~PluginBrowser() = default; -private: - QWidget * m_view; -}; - - +private slots: + void onFilterChanged( const QString & filter ); +private: + void addPlugins(); + void updateRootVisibility( int index ); + void updateRootVisibilities(); -class PluginDescList : public QWidget -{ - Q_OBJECT -public: - PluginDescList(QWidget* parent); + QWidget * m_view; + QTreeWidget * m_descTree; + QTreeWidgetItem * m_lmmsRoot; + QTreeWidgetItem * m_lv2Root; }; @@ -62,6 +66,7 @@ class PluginDescWidget : public QWidget public: typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; PluginDescWidget( const PluginKey & _pk, QWidget * _parent ); + QString name() const; protected: diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 27747bc5363..1e73b7cdcbf 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -24,11 +24,13 @@ #include "PluginBrowser.h" +#include #include -#include +#include #include -#include +#include #include +#include #include "embed.h" #include "Engine.h" @@ -60,23 +62,91 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) : m_view ); hint->setWordWrap( true ); - QScrollArea* scrollarea = new QScrollArea( m_view ); - PluginDescList* descList = new PluginDescList( m_view ); - scrollarea->setWidget(descList); - scrollarea->setWidgetResizable(true); + QLineEdit * searchBar = new QLineEdit( m_view ); + searchBar->setPlaceholderText( "Search" ); + searchBar->setMaxLength( 64 ); + searchBar->setClearButtonEnabled( true ); + + m_descTree = new QTreeWidget( m_view ); + m_descTree->setColumnCount( 1 ); + m_descTree->header()->setVisible( false ); + m_descTree->setIndentation( 10 ); + m_descTree->setSelectionMode( QAbstractItemView::NoSelection ); + + connect( searchBar, SIGNAL( textEdited( const QString & ) ), + this, SLOT( onFilterChanged( const QString & ) ) ); + + view_layout->addWidget( hint ); + view_layout->addWidget( searchBar ); + view_layout->addWidget( m_descTree ); + + // Add LMMS root to the tree + m_lmmsRoot = new QTreeWidgetItem(); + m_lmmsRoot->setText( 0, "LMMS" ); + m_descTree->insertTopLevelItem( 0, m_lmmsRoot ); + m_lmmsRoot->setExpanded( true ); - view_layout->addWidget(hint); - view_layout->addWidget(scrollarea); + // Add LV2 root to the tree + m_lv2Root = new QTreeWidgetItem(); + m_lv2Root->setText( 0, "LV2" ); + m_descTree->insertTopLevelItem( 1, m_lv2Root ); + + // Add plugins to the tree roots + addPlugins(); + + // Resize + m_descTree->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); + + // Hide empty roots + updateRootVisibilities(); } +void PluginBrowser::updateRootVisibility( int rootIndex ) +{ + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + root->setHidden( !root->childCount() ); +} -PluginDescList::PluginDescList(QWidget *parent) : - QWidget(parent) +void PluginBrowser::updateRootVisibilities() { - QVBoxLayout* layout = new QVBoxLayout(this); + int rootCount = m_descTree->topLevelItemCount(); + for (int rootIndex = 0; rootIndex < rootCount; ++rootIndex) + { + updateRootVisibility( rootIndex ); + } +} + + +void PluginBrowser::onFilterChanged( const QString & filter ) +{ + int rootCount = m_descTree->topLevelItemCount(); + for (int rootIndex = 0; rootIndex < rootCount; ++rootIndex) + { + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + int itemCount = root->childCount(); + for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) + { + QTreeWidgetItem * item = root->child( itemIndex ); + PluginDescWidget * descWidget = static_cast + (m_descTree->itemWidget( item, 0)); + if (descWidget->name().contains(filter, Qt::CaseInsensitive)) + { + item->setHidden( false ); + } + else + { + item->setHidden( true ); + } + } + } +} + + +void PluginBrowser::addPlugins() +{ QList descs = pluginFactory->descriptors(Plugin::Instrument); std::sort( descs.begin(), @@ -93,7 +163,7 @@ PluginDescList::PluginDescList(QWidget *parent) : for (const Plugin::Descriptor* desc: descs) { - if( desc->subPluginFeatures ) + if ( desc->subPluginFeatures ) { desc->subPluginFeatures->listSubPluginKeys( desc, @@ -109,13 +179,18 @@ PluginDescList::PluginDescList(QWidget *parent) : for (const PluginKey& key : pluginKeys) { - PluginDescWidget* p = new PluginDescWidget( key, this ); - p->show(); - layout->addWidget(p); + QTreeWidgetItem * item = new QTreeWidgetItem(); + if ( key.desc->name == QStringLiteral("lv2instrument") ) + { + m_lv2Root->addChild( item ); + } + else + { + m_lmmsRoot->addChild( item ); + } + PluginDescWidget* p = new PluginDescWidget( key, m_descTree ); + m_descTree->setItemWidget( item, 0, p ); } - - setLayout(layout); - layout->addStretch(); } @@ -137,6 +212,14 @@ PluginDescWidget::PluginDescWidget(const PluginKey &_pk, +QString PluginDescWidget::name() const +{ + return m_pluginKey.displayName(); +} + + + + void PluginDescWidget::paintEvent( QPaintEvent * ) { @@ -190,7 +273,7 @@ void PluginDescWidget::leaveEvent( QEvent * _e ) void PluginDescWidget::mousePressEvent( QMouseEvent * _me ) { - if( _me->button() == Qt::LeftButton ) + if ( _me->button() == Qt::LeftButton ) { Engine::setDndPluginKey(&m_pluginKey); new StringPairDrag("instrument", From 7c148e50476f14d9800259a0f5e047265bc92dbb Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 7 Apr 2019 22:55:13 +0200 Subject: [PATCH 014/120] Do not delete help window on close --- src/gui/Lv2ViewBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index c03732fc0c8..8506fe6a299 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -174,6 +174,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) m_helpWindow = gui->mainWindow()->addWindowedWidget(infoLabel); m_helpWindow->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + m_helpWindow->setAttribute(Qt::WA_DeleteOnClose, false); m_helpWindow->hide(); break; From c168b6ff6f4cb7d95f8be310f69ed1d13bcf3ada Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 15:39:15 +0100 Subject: [PATCH 015/120] Add PluginIssue class --- include/PluginIssue.h | 66 ++++++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/PluginIssue.cpp | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 include/PluginIssue.h create mode 100644 src/core/PluginIssue.cpp diff --git a/include/PluginIssue.h b/include/PluginIssue.h new file mode 100644 index 00000000000..9207acf0ede --- /dev/null +++ b/include/PluginIssue.h @@ -0,0 +1,66 @@ +/* + * PluginIssue.h - PluginIssue class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 PLUGINISSUE_H +#define PLUGINISSUE_H + +#include +#include + +//! Types of issues that can cause LMMS to not load a plugin +//! LMMS Plugins should use this to indicate errors +enum PluginIssueType +{ + unknownPortFlow, + unknownPortType, + tooManyInputChannels, + tooManyOutputChannels, + noOutputChannel, + portHasNoDef, + portHasNoMin, + portHasNoMax, + featureNotSupported, + badPortType, + noIssue +}; + +//! Issue type bundled with informational string +class PluginIssue +{ + static const char* msgFor(const PluginIssueType& it); + + PluginIssueType m_issueType; + std::string m_info; + +public: + PluginIssue(PluginIssueType it, std::string msg = std::string()) + : m_issueType(it), m_info(msg) + { + } + friend QDebug operator<<(QDebug stream, const PluginIssue& iss); +}; + +QDebug operator<<(QDebug stream, const PluginIssue& iss); + +#endif // PLUGINISSUE_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7870415f971..cc8bfd34c5a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -48,6 +48,7 @@ set(LMMS_SRCS core/Piano.cpp core/PlayHandle.cpp core/Plugin.cpp + core/PluginIssue.cpp core/PluginFactory.cpp core/PresetPreviewPlayHandle.cpp core/ProjectJournal.cpp diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp new file mode 100644 index 00000000000..4a88feb028c --- /dev/null +++ b/src/core/PluginIssue.cpp @@ -0,0 +1,72 @@ +/* + * PluginIssue.h - PluginIssue class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 + +#include "PluginIssue.h" + +const char *PluginIssue::msgFor(const PluginIssueType &it) +{ + switch (it) + { + case unknownPortFlow: + return "unknown port flow for mandatory port"; + case unknownPortType: + return "unknown port type for mandatory port"; + case tooManyInputChannels: + return "too many audio input channels"; + case tooManyOutputChannels: + return "too many audio output channels"; + case noOutputChannel: + return "no audio output channel"; + case portHasNoDef: + return "port is missing default value"; + case portHasNoMin: + return "port is missing min value"; + case portHasNoMax: + return "port is missing max value"; + case featureNotSupported: + return "required feature not supported"; + case badPortType: + return "unsupported port Type"; + case noIssue: + return nullptr; + } + return nullptr; +} + + + + +QDebug operator<<(QDebug stream, const PluginIssue &iss) +{ + stream << PluginIssue::msgFor(iss.m_issueType); + if(iss.m_info.length()) + { + stream.nospace() << ": " << iss.m_info.c_str(); + } + return stream; +} + + From f3bb002b935e13b703555ad7c18e729549528621 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 17 Apr 2019 18:23:28 +0200 Subject: [PATCH 016/120] Editorial fixes after review --- include/PluginIssue.h | 4 ++-- src/core/PluginIssue.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 9207acf0ede..c009458056e 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -40,8 +40,8 @@ enum PluginIssueType portHasNoDef, portHasNoMin, portHasNoMax, - featureNotSupported, - badPortType, + featureNotSupported, //!< plugin requires functionality LMMS can't offer + badPortType, //!< port type not supported noIssue }; diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 4a88feb028c..4a8b2ee5b4e 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -49,7 +49,7 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) case featureNotSupported: return "required feature not supported"; case badPortType: - return "unsupported port Type"; + return "unsupported port type"; case noIssue: return nullptr; } @@ -62,7 +62,7 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) QDebug operator<<(QDebug stream, const PluginIssue &iss) { stream << PluginIssue::msgFor(iss.m_issueType); - if(iss.m_info.length()) + if (iss.m_info.length()) { stream.nospace() << ": " << iss.m_info.c_str(); } From e9bf4314654f9212c285ab709ff81e7780064743 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Apr 2019 09:04:18 +0200 Subject: [PATCH 017/120] Fix memory cleanup --- include/LinkedModelGroupViews.h | 15 ++++++++---- src/gui/widgets/LinkedModelGroupViews.cpp | 29 ++++++++++++----------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index a48c70b2e6c..cd8936af052 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -26,6 +26,8 @@ #define LINKEDMODELGROUPVIEWS_H +#include +#include #include @@ -59,8 +61,8 @@ class LinkedModelGroupViewBase : public QGroupBox int m_colNum; //!< column number in surrounding grid in Lv2ViewBase bool m_isLinking; class QGridLayout* m_grid; - QVector m_controls; - QVector m_leds; + std::vector> m_controls; + std::vector> m_leds; }; @@ -71,19 +73,22 @@ class LinkedModelGroupsViewBase protected: //! @param pluginWidget A child class which inherits QWidget LinkedModelGroupsViewBase(class LinkedModelGroups *ctrlBase); - ~LinkedModelGroupsViewBase(); + ~LinkedModelGroupsViewBase() = default; //! Reconnect models if model changed; to be called by child virtuals void modelChanged(class LinkedModelGroups* ctrlBase); //! Access to the global multi channel link LED - LedCheckBox* globalLinkLed() { return m_multiChannelLink; } + LedCheckBox* globalLinkLed() { return m_multiChannelLink.get(); } private: //! The base class must return the adressed group view virtual LinkedModelGroupViewBase* getGroupView(std::size_t idx) = 0; - class LedCheckBox *m_multiChannelLink = nullptr; + // Implement deletion in the CPP file: + struct MultiChannelLinkDeleter { void operator()(LedCheckBox* l); }; + std::unique_ptr + m_multiChannelLink = nullptr; }; diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 3a47ce9cc59..16c3f547a09 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -75,9 +75,9 @@ LinkedModelGroupViewBase::~LinkedModelGroupViewBase() {} void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) { // reconnect models - QVector::Iterator itr = m_controls.begin(); + std::vector>::iterator itr = m_controls.begin(); std::vector models = group->models(); - Q_ASSERT(m_controls.size() == static_cast(models.size())); + Q_ASSERT(m_controls.size() == models.size()); for(AutomatableModel* mdl : models) { @@ -85,7 +85,7 @@ void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) } std::size_t count = 0; - for (LedCheckBox* led : m_leds) + for (std::unique_ptr& led : m_leds) { led->setModel(group->linkEnabledModel(count++)); } @@ -97,19 +97,20 @@ void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) { int colNum2 = m_colNum * (1 + m_isLinking); - int wdgNum = m_controls.size() * (1 + m_isLinking); + int wdgNum = static_cast(m_controls.size() * (1 + m_isLinking)); if(ctrl) { int x = wdgNum%colNum2, y = wdgNum/colNum2; // start in row one, add widgets cell by cell if(m_isLinking) { - LedCheckBox* cb = new LedCheckBox(nullptr); + LedCheckBox* cb = new LedCheckBox(qobject_cast( + ctrl->topWidget()->parent())); m_grid->addWidget(cb, y, x); - m_leds.push_back(cb); + m_leds.push_back(std::unique_ptr(cb)); } - m_controls.push_back(ctrl); + m_controls.push_back(std::unique_ptr(ctrl)); m_grid->addWidget(ctrl->topWidget(), y, x + 1, Qt::AlignCenter); wdgNum += m_isLinking; ++wdgNum; @@ -160,19 +161,14 @@ LinkedModelGroupsViewBase::LinkedModelGroupsViewBase( { if(ctrlBase->multiChannelLinkModel()) { - m_multiChannelLink = new LedCheckBox(QObject::tr("Link Channels"), - nullptr); + m_multiChannelLink.reset(new LedCheckBox(QObject::tr("Link Channels"), + nullptr)); } } -LinkedModelGroupsViewBase::~LinkedModelGroupsViewBase() {} - - - - void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) { if(groups->multiChannelLinkModel()) @@ -187,3 +183,8 @@ void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) } + + +void LinkedModelGroupsViewBase::MultiChannelLinkDeleter::operator()(LedCheckBox *l) { delete l; } + + From a396ae4b327b88c266153a47677e9374d1d89a66 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Apr 2019 09:17:58 +0200 Subject: [PATCH 018/120] Fix review issues Fix almost everything from initial review from @DomClark from #4899 . Mostly memory cleanup. --- include/Lv2Basics.h | 31 ++++++--- include/Lv2ControlBase.h | 7 +- include/Lv2Manager.h | 48 ++++--------- include/Lv2Proc.h | 8 +-- include/Lv2SubPluginFeatures.h | 1 + plugins/Lv2Effect/CMakeLists.txt | 6 +- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- plugins/Lv2Effect/Lv2FxControls.cpp | 4 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 11 ++- src/core/CMakeLists.txt | 1 + src/core/lv2/Lv2Basics.cpp | 45 ++++++++++++ src/core/lv2/Lv2ControlBase.cpp | 21 +++--- src/core/lv2/Lv2Manager.cpp | 22 ++++-- src/core/lv2/Lv2Ports.cpp | 20 +++--- src/core/lv2/Lv2Proc.cpp | 93 +++++++++++++------------ src/core/lv2/Lv2SubPluginFeatures.cpp | 28 ++++---- src/gui/Lv2ViewBase.cpp | 32 +++++---- 17 files changed, 216 insertions(+), 164 deletions(-) create mode 100644 src/core/lv2/Lv2Basics.cpp diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index c49fa9261f9..12ff50e6f00 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -32,19 +32,34 @@ #ifdef LMMS_HAVE_LV2 #include +#include +#include +struct LilvNodeDeleter +{ + void operator()(LilvNode* n) { lilv_node_free(n); } +}; -//! a simple RAII class for lilv nodes that shall be freed -struct AutoLilvNode +struct LilvNodesDeleter { - LilvNode* n; - AutoLilvNode(LilvNode* n) : n(n) {} - AutoLilvNode(const AutoLilvNode& other) = delete; - AutoLilvNode(AutoLilvNode&& other) { n = other.n; other.n = nullptr; } - ~AutoLilvNode() { if (n) { lilv_node_free(n); } } - const LilvNode* get() const { return n; } + void operator()(LilvNodes* n) { lilv_nodes_free(n); } }; +using AutoLilvNode = std::unique_ptr; +using AutoLilvNodes = std::unique_ptr; + +/** + Return QString from a plugin's node, everything will be freed automatically + @param plug The plugin where the node is + @param getFun The function to return the node from the plugin + @param convFunc convFunc The plugin to return a char pointer from that node; + this is usually lilv_node_as_string or lilv_node_as_uri +*/ +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); #endif // LMMS_HAVE_LV2 #endif // LV2BASICS_H diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 6d357a4f063..174cef27f20 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -77,13 +77,14 @@ class Lv2ControlBase : public LinkedModelGroups bool hasGui() const { return m_hasGUI; } void setHasGui(bool val) { m_hasGUI = val; } - std::vector& controls() { return m_procs; } + std::vector>& controls() { return m_procs; } protected: /* ctor/dtor */ - //! @param parent the class inheriting this class + //! @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(Model *that, const QString& uri); virtual ~Lv2ControlBase() override; @@ -131,7 +132,7 @@ class Lv2ControlBase : public LinkedModelGroups //! 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 m_procs; + std::vector> m_procs; bool m_valid = true; bool m_hasGUI = false; diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index de6f3367de5..2815799cffa 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -42,7 +42,7 @@ explanation: "x = {y z}" means class "x" consists of classes "y" and "z" (and probably other classes not mentioned) - "x = {y*}" me}ns class "x" references/uses class "y" + "x = {y*}" means class "x" references/uses class "y" core: Lv2Proc = {LilvInstance} @@ -50,10 +50,10 @@ Lv2Manager = {LilvPlugin*, LilvPlugin* ...} (creates Lv2ControlBase, Lv2ControlBase...) - Lv2FxControls = {LilvControlBase} + Lv2FxControls = {Lv2ControlBase} Lv2Effect = {Effect + Lv2FxControls} (takes Lv2SubPluginFeatures in ctor) - Lv2Instrument = {Instrument + LilvControlBase} + Lv2Instrument = {Instrument + Lv2ControlBase} (takes Lv2SubPluginFeatures in ctor) gui: @@ -82,10 +82,7 @@ class Lv2Manager ~Lv2Manager(); - AutoLilvNode uri(const char* uriStr) - { - return AutoLilvNode(lilv_new_uri(m_world, uriStr)); - } + AutoLilvNode uri(const char* uriStr); //! Class representing info for one plugin struct Lv2Info @@ -97,44 +94,25 @@ class Lv2Manager Lv2Info(const LilvPlugin* plug, Plugin::PluginTypes type, bool valid) : m_plugin(plug), m_type(type), m_valid(valid) {} Lv2Info(const Lv2Info &) = delete; - Lv2Info(Lv2Info&& other) : - m_plugin(other.m_plugin), - m_type(std::move(other.m_type)), - m_valid(std::move(other.m_valid)) - { - } - Lv2Info& operator=(Lv2Info&& other) - { - m_plugin = other.m_plugin; - m_type = std::move(other.m_type); - m_valid = std::move(other.m_valid); - return *this; - } - bool isValid() const { return m_valid; } - Plugin::PluginTypes type() const { return m_type; } + Lv2Info(Lv2Info&& other) = default; + Lv2Info& operator=(Lv2Info&& other) = default; + const LilvPlugin* plugin() const { return m_plugin; } + Plugin::PluginTypes type() const { return m_type; } + bool isValid() const { return m_valid; } + private: const LilvPlugin* m_plugin; Plugin::PluginTypes m_type; bool m_valid = false; }; - //! Return a descriptor with @p uniqueName or nullptr if none exists - //! @param uniqueName The lv2::unique_name of the plugin + //! Return descriptor with URI @p uri or nullptr if none exists const LilvPlugin *getPlugin(const std::string &uri); + //! Return descriptor with URI @p uri or nullptr if none exists const LilvPlugin *getPlugin(const QString uri); - class Iterator - { - std::map::iterator m_itr; - public: - bool operator!=(const Iterator &other) { return m_itr != other.m_itr; } - Iterator &operator++() { ++m_itr; return *this; } - std::pair &operator*() { return *m_itr; } - Iterator(std::map::iterator itr) : - m_itr(itr) {} - }; - + using Iterator = std::map::iterator; Iterator begin() { return Iterator(m_lv2InfoMap.begin()); } Iterator end() { return Iterator(m_lv2InfoMap.end()); } diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 0b65a27d369..7285d1ce55c 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -82,8 +82,8 @@ class Lv2Proc : public LinkedModelGroup const StereoPortRef& inPorts() const { return m_inPorts; } StereoPortRef& outPorts() { return m_outPorts; } const StereoPortRef& outPorts() const { return m_outPorts; } - std::vector& getPorts() { return m_ports; } - const std::vector& getPorts() const { return m_ports; } + std::vector>& getPorts() { return m_ports; } + const std::vector>& getPorts() const { return m_ports; } //! Debug function to print ports to stdout void dumpPorts(); @@ -127,7 +127,7 @@ class Lv2Proc : public LinkedModelGroup const LilvPlugin* m_plugin; LilvInstance* m_instance; - std::vector m_ports; + std::vector> m_ports; StereoPortRef m_inPorts, m_outPorts; std::size_t m_controlCount = 0; @@ -142,8 +142,6 @@ class Lv2Proc : public LinkedModelGroup void createPort(unsigned portNum); //! connect m_ports[portNum] with Lv2 void connectPort(unsigned num); - //! clean up all ports - void destroyPorts(); void dumpPort(std::size_t num); diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h index 30d6dad8a35..d36c9023f96 100644 --- a/include/Lv2SubPluginFeatures.h +++ b/include/Lv2SubPluginFeatures.h @@ -39,6 +39,7 @@ class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures { private: static const LilvPlugin *getPlugin(const Key &k); + static QString pluginName(const LilvPlugin *plug); public: Lv2SubPluginFeatures(Plugin::PluginTypes _type); diff --git a/plugins/Lv2Effect/CMakeLists.txt b/plugins/Lv2Effect/CMakeLists.txt index 7f146a0a5b6..a0db20656a3 100644 --- a/plugins/Lv2Effect/CMakeLists.txt +++ b/plugins/Lv2Effect/CMakeLists.txt @@ -1,7 +1,7 @@ IF(LMMS_HAVE_LV2) - INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) + INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) INCLUDE(BuildPlugin) BUILD_PLUGIN(lv2effect Lv2Effect.cpp Lv2FxControls.cpp Lv2FxControlDialog.cpp Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h MOCFILES Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h EMBEDDED_RESOURCES logo.png) ENDIF(LMMS_HAVE_LV2) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 0090733054a..3c44b65f1bd 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -98,7 +98,7 @@ PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) { using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; Lv2Effect* eff = new Lv2Effect(_parent, static_cast(_data)); - if (!eff->isValid()) { eff = nullptr; } + if (!eff->isValid()) { delete eff; eff = nullptr; } return eff; } diff --git a/plugins/Lv2Effect/Lv2FxControls.cpp b/plugins/Lv2Effect/Lv2FxControls.cpp index ef6f710c8ee..632ef5e9d94 100644 --- a/plugins/Lv2Effect/Lv2FxControls.cpp +++ b/plugins/Lv2Effect/Lv2FxControls.cpp @@ -45,9 +45,9 @@ Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) : SLOT(reloadPlugin())); if(multiChannelLinkModel()) { connect(multiChannelLinkModel(), SIGNAL(dataChanged()), - this, SLOT(updateLinkStatesFromGlobal())); + this, SLOT(updateLinkStatesFromGlobal()), Qt::DirectConnection); connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), - this, SLOT(linkPort(int, bool))); + this, SLOT(linkPort(int, bool)), Qt::DirectConnection); } } } diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index f818d080e6c..4d6cac35384 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -79,14 +79,14 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, for (int i = 0; i < NumKeys; ++i) { m_runningNotes[i] = 0; } #endif connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()), - this, SLOT(updatePitchRange())); + this, SLOT(updatePitchRange()), Qt::DirectConnection); connect(Engine::mixer(), SIGNAL(sampleRateChanged()), this, SLOT(reloadPlugin())); if (multiChannelLinkModel()) { connect(multiChannelLinkModel(), SIGNAL(dataChanged()), - this, SLOT(updateLinkStatesFromGlobal())); + this, SLOT(updateLinkStatesFromGlobal()), Qt::DirectConnection); connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), - this, SLOT(linkPort(int, bool))); + this, SLOT(linkPort(int, bool)), Qt::DirectConnection); } // now we need a play-handle which cares for calling play() @@ -174,8 +174,7 @@ void Lv2Instrument::play(sampleFrame *buf) copyBuffersToLmms(buf, fpp); - instrumentTrack()->processAudioBuffer( - buf, Engine::mixer()->framesPerPeriod(), nullptr); + instrumentTrack()->processAudioBuffer(buf, fpp, nullptr); } @@ -344,7 +343,7 @@ PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *_parent, void *_data) Lv2Instrument* ins = new Lv2Instrument( static_cast(_parent), static_cast(_data )); - if (!ins->isValid()) { ins = nullptr; } + if (!ins->isValid()) { delete ins; ins = nullptr; } return ins; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a4bc08c5e30..a3f14a2b7bf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -89,6 +89,7 @@ set(LMMS_SRCS core/audio/AudioSampleRecorder.cpp core/audio/AudioSdl.cpp + core/lv2/Lv2Basics.cpp core/lv2/Lv2ControlBase.cpp core/lv2/Lv2Ports.cpp core/lv2/Lv2Proc.cpp diff --git a/src/core/lv2/Lv2Basics.cpp b/src/core/lv2/Lv2Basics.cpp new file mode 100644 index 00000000000..6cce3367877 --- /dev/null +++ b/src/core/lv2/Lv2Basics.cpp @@ -0,0 +1,45 @@ +/* + * Lv2Basics.cpp - basic Lv2 functions + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "Lv2ControlBase.h" + +#ifdef LMMS_HAVE_LV2 + +#include "Lv2Basics.h" + +QString qStringFromPluginNode(const LilvPlugin* plug, + LilvNode* (*getFunc)(const LilvPlugin*)) +{ + auto conv = lilv_node_as_string; + return QString::fromUtf8(conv(AutoLilvNode((*getFunc)(plug)).get())); +} + +QString qStringFromPortName(const LilvPlugin* plug, const LilvPort* port) +{ + return QString::fromUtf8( + lilv_node_as_string(AutoLilvNode(lilv_port_get_name(plug, port)).get())); +} + +#endif // LMMS_HAVE_LV2 + diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 1e564cea5b3..449b4c7ccb6 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -54,14 +54,14 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo while (channelsLeft > 0) { - Lv2Proc* newOne = new Lv2Proc(m_plugin, that); + std::unique_ptr newOne(new Lv2Proc(m_plugin, that)); if (newOne->isValid()) { channelsLeft -= std::max( 1 + static_cast(newOne->inPorts().m_right), 1 + static_cast(newOne->outPorts().m_right)); Q_ASSERT(channelsLeft >= 0); - m_procs.push_back(newOne); + m_procs.push_back(std::move(newOne)); } else { @@ -95,24 +95,21 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : -Lv2ControlBase::~Lv2ControlBase() -{ - for (Lv2Proc* c : m_procs) { delete c; } -} +Lv2ControlBase::~Lv2ControlBase() {} LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) { - return (m_procs.size() > idx) ? m_procs[idx] : nullptr; + return (m_procs.size() > idx) ? m_procs[idx].get() : nullptr; } void Lv2ControlBase::copyModelsFromLmms() { - for (Lv2Proc* c : m_procs) { c->copyModelsFromCore(); } + for (std::unique_ptr& c : m_procs) { c->copyModelsFromCore(); } } @@ -120,7 +117,7 @@ void Lv2ControlBase::copyModelsFromLmms() { void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { unsigned offset = 0; - for (Lv2Proc* c : m_procs) { + for (std::unique_ptr& c : m_procs) { c->copyBuffersFromCore(buf, offset, m_channelsPerProc, frames); offset += m_channelsPerProc; } @@ -131,7 +128,7 @@ void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { unsigned offset = 0; - for (const Lv2Proc* c : m_procs) { + for (const std::unique_ptr& c : m_procs) { c->copyBuffersToCore(buf, offset, m_channelsPerProc, frames); offset += m_channelsPerProc; } @@ -141,7 +138,7 @@ void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { void Lv2ControlBase::run(unsigned frames) { - for (Lv2Proc* c : m_procs) { c->run(frames); } + for (std::unique_ptr& c : m_procs) { c->run(frames); } } @@ -183,7 +180,7 @@ void Lv2ControlBase::reloadPlugin() std::size_t Lv2ControlBase::controlCount() const { std::size_t res = 0; - for (const Lv2Proc* c : m_procs) { res += c->controlCount(); } + for (const std::unique_ptr& c : m_procs) { res += c->controlCount(); } return res; } diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 4f3dfe8e4f2..a49c73eade9 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -26,12 +26,11 @@ #ifdef LMMS_HAVE_LV2 +#include +#include #include #include #include -#include - -#include #include "ConfigManager.h" #include "Plugin.h" @@ -53,6 +52,15 @@ Lv2Manager::Lv2Manager() Lv2Manager::~Lv2Manager() { + lilv_world_free(m_world); +} + + + + +AutoLilvNode Lv2Manager::uri(const char *uriStr) +{ + return AutoLilvNode(lilv_new_uri(m_world, uriStr)); } @@ -100,7 +108,7 @@ bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { const LilvPluginClasses* allClasses = lilv_world_get_plugin_classes(m_world); const LilvPluginClass* root = lilv_world_get_plugin_class(m_world); - const LilvPluginClass* gen = lilv_plugin_classes_get_by_uri(allClasses, + const LilvPluginClass* search = lilv_plugin_classes_get_by_uri(allClasses, uri(uriStr).get()); auto clssEq = [](const LilvPluginClass* pc1, @@ -110,13 +118,13 @@ bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) lilv_plugin_class_get_uri(pc1), lilv_plugin_class_get_uri(pc2)); }; - bool isGen = false; + bool isFound = false; for (; - clssEq(clvss, root) && (isGen = clssEq(clvss, gen)); + !(isFound = clssEq(clvss, search)) && !clssEq(clvss, root); clvss = lilv_plugin_classes_get_by_uri(allClasses, lilv_plugin_class_get_parent_uri(clvss)) ) ; - return isGen; + return isFound; } diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 4ffddb7cb8e..c5a454f2ef4 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -85,19 +85,17 @@ std::vector Meta::get(const LilvPlugin *plugin, unsigned int portNum) { std::vector portIssues; - auto issue = [&portIssues](PluginIssueType i, const char* msg = "") { - portIssues.emplace_back(i, msg); }; + auto issue = [&portIssues](PluginIssueType i, std::string msg = "") { + portIssues.emplace_back(i, std::move(msg)); }; Lv2Manager* man = Engine::getLv2Manager(); - AutoLilvNode connectionOptional = man->uri(LV2_CORE__connectionOptional); const LilvPort* lilvPort = lilv_plugin_get_port_by_index(plugin, portNum); auto portFunc = [&plugin, &lilvPort, &man]( bool (*fptr)(const LilvPlugin*, const LilvPort*, const LilvNode*), const char* str) { - bool res = fptr(plugin, lilvPort, man->uri(str).get()); - return res; + return fptr(plugin, lilvPort, man->uri(str).get()); }; auto hasProperty = [&portFunc](const char* str) { @@ -105,11 +103,13 @@ std::vector Meta::get(const LilvPlugin *plugin, auto isA = [&portFunc](const char* str) { return portFunc(lilv_port_is_a, str); }; + std::string portName; + { + AutoLilvNode nameNode(lilv_port_get_name(plugin, lilvPort)); + portName = lilv_node_as_string(nameNode.get()); + } - const char* portName = lilv_node_as_string(lilv_port_get_name(plugin, lilvPort)); - - m_optional = lilv_port_has_property(plugin, lilvPort, - connectionOptional.get()); + m_optional = hasProperty(LV2_CORE__connectionOptional); m_vis = hasProperty(LV2_CORE__integer) ? Vis::Integer // WARNING: this may still be changed below @@ -184,7 +184,7 @@ std::vector Meta::get(const LilvPlugin *plugin, QString PortBase::name() const { - AutoLilvNode node = lilv_port_get_name(m_plugin, m_port); + AutoLilvNode node(lilv_port_get_name(m_plugin, m_port)); QString res = lilv_node_as_string(node.get()); return res; } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index f8386499112..51e23d5413d 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -42,8 +42,8 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, std::vector& issues, bool printIssues) { unsigned maxPorts = lilv_plugin_get_num_ports(plugin); - enum { checkIn, checkOut, checkMax }; - unsigned audioChannels[checkMax] = { 0, 0 }; // input and output count + enum { inCount, outCount, maxCount }; + unsigned audioChannels[maxCount] = { 0, 0 }; // input and output count for (unsigned portNum = 0; portNum < maxPorts; ++portNum) { @@ -55,36 +55,39 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, if (meta.m_type == Lv2Ports::Type::Audio && !portIsSideChain(plugin, lilv_plugin_get_port_by_index(plugin, portNum))) - ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output]; + ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output + ? outCount : inCount]; } - if (audioChannels[checkIn] > 2) + if (audioChannels[inCount] > 2) issues.emplace_back(tooManyInputChannels, - std::to_string(audioChannels[0])); - if (audioChannels[checkOut] == 0) + std::to_string(audioChannels[inCount])); + if (audioChannels[outCount] == 0) issues.emplace_back(noOutputChannel); - else if (audioChannels[checkOut] > 2) + else if (audioChannels[outCount] > 2) issues.emplace_back(tooManyOutputChannels, - std::to_string(audioChannels[1])); + std::to_string(audioChannels[outCount])); - const LilvNodes* reqFeats = lilv_plugin_get_required_features(plugin); - LILV_FOREACH (nodes, itr, reqFeats) + AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); + LILV_FOREACH (nodes, itr, reqFeats.get()) { issues.emplace_back(featureNotSupported, - lilv_node_as_string(lilv_nodes_get(reqFeats, itr))); + lilv_node_as_string(lilv_nodes_get(reqFeats.get(), itr))); } if (printIssues && issues.size()) { - qDebug() << "Lv2 plugin " << - lilv_node_as_string(lilv_plugin_get_name(plugin)) - << " can not be loaded:"; + qDebug() << "Lv2 plugin" + << qStringFromPluginNode(plugin, lilv_plugin_get_name) + << "(URI:" + << lilv_node_as_uri(lilv_plugin_get_uri(plugin)) + << ") can not be loaded:"; for (const PluginIssue& iss : issues) { qDebug() << " - " << iss; } } - return (audioChannels[1] > 2 || audioChannels[0] > 2) + return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2) ? Plugin::Undefined - : audioChannels[0] + : (audioChannels[inCount] > 0) ? Plugin::Effect : Plugin::Instrument; } @@ -118,7 +121,7 @@ Lv2Proc::~Lv2Proc() { shutdownPlugin(); } void Lv2Proc::dumpPorts() { std::size_t num = 0; - for (const Lv2Ports::PortBase* port: m_ports) + for (const std::unique_ptr& port: m_ports) { (void)port; dumpPort(num++); @@ -168,7 +171,8 @@ void Lv2Proc::copyModelsFromCore() } } copy; - for (Lv2Ports::PortBase* port : m_ports) { port->accept(copy); } + for (const std::unique_ptr& port : m_ports) { + port->accept(copy); } } @@ -216,7 +220,7 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf, void Lv2Proc::run(unsigned frames) { - lilv_instance_run(m_instance, static_cast(frames)); + lilv_instance_run(m_instance, frames); } @@ -252,7 +256,10 @@ void Lv2Proc::initPlugin() else { qCritical() << "Failed to create an instance of" - << lilv_node_as_string(lilv_plugin_get_name(m_plugin)); + << qStringFromPluginNode(m_plugin, lilv_plugin_get_name) + << "(URI:" + << lilv_node_as_uri(lilv_plugin_get_uri(m_plugin)) + << ")"; m_valid = false; } } @@ -263,6 +270,7 @@ void Lv2Proc::initPlugin() void Lv2Proc::shutdownPlugin() { lilv_instance_deactivate(m_instance); + lilv_instance_free(m_instance); m_instance = nullptr; } @@ -279,22 +287,19 @@ void Lv2Proc::loadFileInternal(const QString &file) void Lv2Proc::createPort(unsigned portNum) { - Lv2Ports::PortBase*& port = m_ports[portNum]; - Lv2Ports::Meta meta; meta.get(m_plugin, portNum); const LilvPort* lilvPort = lilv_plugin_get_port_by_index(m_plugin, portNum); - + Lv2Ports::PortBase* port; if (meta.m_type == Lv2Ports::Type::Control) { Lv2Ports::Control* ctrl = new Lv2Ports::Control; if (meta.m_flow == Lv2Ports::Flow::Input) { - LilvNode* node = lilv_port_get_name(m_plugin, lilvPort); - QString dispName = lilv_node_as_string(node); - lilv_node_free(node); + AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); + QString dispName = lilv_node_as_string(node.get()); switch (meta.m_vis) { case Lv2Ports::Vis::None: @@ -324,7 +329,7 @@ void Lv2Proc::createPort(unsigned portNum) ComboBoxModel* comboModel = new ComboBoxModel( nullptr, dispName); - const LilvScalePoints* sps = + LilvScalePoints* sps = lilv_port_get_scale_points(m_plugin, lilvPort); LILV_FOREACH(scale_points, i, sps) { @@ -335,6 +340,7 @@ void Lv2Proc::createPort(unsigned portNum) lilv_node_as_string( lilv_scale_point_get_label(sp))); } + lilv_scale_points_free(sps); ctrl->m_connectedModel.reset(comboModel); break; } @@ -360,9 +366,14 @@ void Lv2Proc::createPort(unsigned portNum) port = new Lv2Ports::Unknown; } - *dynamic_cast(port) = meta; + // `meta` is of class `Lv2Ports::Meta` and `port` is of a child class + // we can now assign the `Lv2Ports::Meta` part of meta to ports, leaving + // the additional members of `port` unchanged + *static_cast(port) = meta; port->m_port = lilvPort; port->m_plugin = m_plugin; + + m_ports[portNum].reset(port); } @@ -374,20 +385,20 @@ void Lv2Proc::createPorts() // i.e. link their data or count them struct RegisterPort : public Lv2Ports::Visitor { - Lv2Proc* proc; + Lv2Proc* m_proc; void visit(Lv2Ports::Control& ctrl) override { if (ctrl.m_flow == Lv2Ports::Flow::Input) { AutomatableModel* amo = ctrl.m_connectedModel.get(); - proc->m_connectedModels.emplace( + m_proc->m_connectedModels.emplace( lilv_node_as_string(lilv_port_get_symbol( - proc->m_plugin, ctrl.m_port)), + m_proc->m_plugin, ctrl.m_port)), amo); - proc->addModel(amo); + m_proc->addModel(amo); - ++proc->m_controlCount; + ++m_proc->m_controlCount; } } @@ -400,10 +411,10 @@ void Lv2Proc::createPorts() switch (audio.m_flow) { case Lv2Ports::Flow::Input: - portRef = &proc->m_inPorts; + portRef = &m_proc->m_inPorts; break; case Lv2Ports::Flow::Output: - portRef = &proc->m_outPorts; + portRef = &m_proc->m_outPorts; break; case Lv2Ports::Flow::Unknown: break; @@ -422,7 +433,7 @@ void Lv2Proc::createPorts() { createPort(portNum); RegisterPort registerPort; - registerPort.proc = this; + registerPort.m_proc = this; m_ports[portNum]->accept(registerPort); } @@ -463,14 +474,6 @@ void Lv2Proc::connectPort(unsigned num) -void Lv2Proc::destroyPorts() -{ - for (Lv2Ports::PortBase* p: m_ports) { delete p; } -} - - - - void Lv2Proc::dumpPort(std::size_t num) { struct DumpPortDetail : public Lv2Ports::ConstVisitor @@ -493,7 +496,7 @@ void Lv2Proc::dumpPort(std::size_t num) const Lv2Ports::PortBase& port = *m_ports[num]; qDebug().nospace() << "port " << num << ":"; qDebug() << " name:" - << lilv_node_as_string(lilv_port_get_name(m_plugin, port.m_port)); + << qStringFromPortName(m_plugin, port.m_port); qDebug() << " flow: " << Lv2Ports::toStr(port.m_flow); qDebug() << " type: " << Lv2Ports::toStr(port.m_type); qDebug() << " visualization: " << Lv2Ports::toStr(port.m_vis); diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 3960c1e67d3..8506b1bfa35 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -43,6 +43,7 @@ #include "Engine.h" #include "Mixer.h" #include "PluginFactory.h" +#include "Lv2Basics.h" #include "Lv2Manager.h" #include "embed.h" @@ -59,6 +60,14 @@ const LilvPlugin *Lv2SubPluginFeatures::getPlugin( +QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug) +{ + return qStringFromPluginNode(plug, lilv_plugin_get_name); +} + + + + Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes _type) : SubPluginFeatures(_type) { @@ -73,8 +82,7 @@ void Lv2SubPluginFeatures::fillDescriptionWidget( const LilvPlugin *plug = getPlugin(*k); QLabel *label = new QLabel(_parent); - label->setText(QWidget::tr("Name: ") + - lilv_node_as_string(lilv_plugin_get_name(plug))); + label->setText(QWidget::tr("Name: ") + pluginName(plug)); QLabel *label2 = new QLabel(_parent); label2->setText(QWidget::tr("URI: ") + @@ -91,7 +99,7 @@ void Lv2SubPluginFeatures::fillDescriptionWidget( QLabel *maker_content = new QLabel(maker); maker_content->setText( - lilv_node_as_string(lilv_plugin_get_author_name(plug))); + qStringFromPluginNode(plug, lilv_plugin_get_author_name)); maker_content->setWordWrap(true); l->addWidget(maker_label); @@ -113,9 +121,8 @@ void Lv2SubPluginFeatures::fillDescriptionWidget( l->addWidget(copyright_label); l->addWidget(copyright_content, 1); - const LilvNodes* extensions = lilv_plugin_get_extension_data(plug); + AutoLilvNodes extensions(lilv_plugin_get_extension_data(plug)); (void)extensions; - // possibly TODO: version, project, plugin type, number of channels } @@ -128,7 +135,7 @@ QString Lv2SubPluginFeatures::additionalFileExtensions( (void)k; // lv2 only loads .lv2 files // maybe add conversions later, e.g. for loading xmz - return nullptr; + return QString(); } @@ -137,7 +144,7 @@ QString Lv2SubPluginFeatures::additionalFileExtensions( QString Lv2SubPluginFeatures::displayName( const Plugin::Descriptor::SubPluginFeatures::Key &k) const { - return lilv_node_as_string(lilv_plugin_get_name(getPlugin(k))); + return pluginName(getPlugin(k)); } @@ -147,7 +154,7 @@ QString Lv2SubPluginFeatures::description( const Plugin::Descriptor::SubPluginFeatures::Key &k) const { (void)k; - return "description not implemented yet"; // TODO + return QString::fromUtf8("description not implemented yet"); // TODO } @@ -178,10 +185,7 @@ void Lv2SubPluginFeatures::listSubPluginKeys( atm["uri"] = QString::fromUtf8(pr.first.c_str()); const LilvPlugin* plug = pr.second.plugin(); - - _kl.push_back(KeyType(_desc, - lilv_node_as_string(lilv_plugin_get_name(plug)), - atm)); + _kl.push_back(KeyType(_desc, pluginName(plug), atm)); //qDebug() << "Found LV2 sub plugin key of type" << // m_type << ":" << pr.first.c_str(); } diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 8506fe6a299..32dd5f46407 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -56,10 +56,10 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, { class SetupWidget : public Lv2Ports::Visitor { - AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); public: - QWidget* par; // input - ControlBase* control = nullptr; // output + QWidget* m_par; // input + const AutoLilvNode* m_commentUri; // input + ControlBase* m_control = nullptr; // output void visit(Lv2Ports::Control& port) override { if (port.m_flow == Lv2Ports::Flow::Input) @@ -69,27 +69,27 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, switch (port.m_vis) { case PortVis::None: - control = new KnobControl(par); + m_control = new KnobControl(m_par); break; case PortVis::Integer: - control = new LcdControl((port.m_max <= 9.0f) ? 1 : 2, - par); + m_control = new LcdControl((port.m_max <= 9.0f) ? 1 : 2, + m_par); break; case PortVis::Enumeration: - control = new ComboControl(par); + m_control = new ComboControl(m_par); break; case PortVis::Toggled: - control = new CheckControl(par); + m_control = new CheckControl(m_par); break; } - control->setText(port.name()); + m_control->setText(port.name()); LilvNodes* props = lilv_port_get_value( - port.m_plugin, port.m_port, commentUri.get()); + port.m_plugin, port.m_port, m_commentUri->get()); LILV_FOREACH(nodes, itr, props) { const LilvNode* nod = lilv_nodes_get(props, itr); - control->topWidget()->setToolTip(lilv_node_as_string(nod)); + m_control->topWidget()->setToolTip(lilv_node_as_string(nod)); break; } lilv_nodes_free(props); @@ -97,13 +97,15 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, } }; - for (Lv2Ports::PortBase* port : ctrlBase->getPorts()) + AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); + for (std::unique_ptr& port : ctrlBase->getPorts()) { SetupWidget setup; - setup.par = this; + setup.m_par = this; + setup.m_commentUri = &commentUri; port->accept(setup); - if (setup.control) { addControl(setup.control); } + if (setup.m_control) { addControl(setup.m_control); } } } @@ -187,7 +189,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) for (int i = 0; i < nProcs; ++i) { Lv2ViewProc* vpr = new Lv2ViewProc(meAsWidget, - ctrlBase->controls()[static_cast(i)], + ctrlBase->controls()[static_cast(i)].get(), colsEach, i, nProcs); grid->addWidget(vpr, Rows::ProcRow, i); m_procViews.push_back(vpr); From a9726f4d6aab3dd55c869f12659f5a1c3afefbbc Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Apr 2019 09:49:32 +0200 Subject: [PATCH 019/120] Lv2Effect: Implement checkGate --- plugins/Lv2Effect/Lv2Effect.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 3c44b65f1bd..40046da60bd 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -84,6 +84,15 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) ctrl.copyBuffersToLmms(buf, frames); + double outSum = .0; + for(fpp_t f = 0; f < frames; ++f) + { + double l = static_cast(buf[f][0]); + double r = static_cast(buf[f][1]); + outSum += l*l + r*r; + } + checkGate(outSum); + return isRunning(); } From b0758c1f5a06a1b5f78114e1ccd36cc1155fc6fa Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 23 Mar 2019 19:03:29 +0100 Subject: [PATCH 020/120] Simplify LinkedModelGroups usage --- include/LinkedModelGroupViews.h | 13 ++++++++----- include/LinkedModelGroups.h | 16 +++++++++++++++- src/gui/widgets/LinkedModelGroupViews.cpp | 6 ++++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index cd8936af052..88c9b16ec36 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -42,10 +42,14 @@ class LinkedModelGroupViewBase : public QGroupBox { public: - //! @param colNum numbers of columns for the controls - //! (link LEDs not counted) - LinkedModelGroupViewBase(QWidget *parent, bool isLinking, - int colNum, int curProc, int nProc, const QString &name = QString()); + /** + @param colNum numbers of columns for the controls + (link LEDs not counted) + @param name Name for the group, like "Left" or "Group 1", + automatically set if not given + */ + LinkedModelGroupViewBase(QWidget *parent, class LinkedModelGroup* model, + int colNum, const QString &name = QString()); ~LinkedModelGroupViewBase(); //! Reconnect models if model changed @@ -71,7 +75,6 @@ class LinkedModelGroupViewBase : public QGroupBox class LinkedModelGroupsViewBase { protected: - //! @param pluginWidget A child class which inherits QWidget LinkedModelGroupsViewBase(class LinkedModelGroups *ctrlBase); ~LinkedModelGroupsViewBase() = default; diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index f5b2bcd09d7..7bb0b8a01f1 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -55,7 +55,10 @@ class LinkedModelGroup : public Model Initialization */ //! @param parent model of the LinkedModelGroups class - LinkedModelGroup(Model* parent) : Model(parent) {} + //! @param curProc number of this processor, counted from 0 + //! @param nProc total number of processors + LinkedModelGroup(Model* parent, int curProc, int nProc) : + Model(parent), m_curProc(curProc), m_nProc(nProc) {} //! After all models have been added, make this processor the one which //! will contain link models associated with its controls void makeLinkingProc(); @@ -81,6 +84,12 @@ class LinkedModelGroup : public Model return m_linkEnabled[id]; } std::vector models() { return m_models; } + /* + General + */ + int nProc() const { return m_nProc; } + int curProc() const { return m_curProc; } + protected: //! Register a further model void addModel(class AutomatableModel* model); @@ -94,6 +103,8 @@ private slots: std::vector m_linkEnabled; //! models for the controls; the vector defines indices for the controls std::vector m_models; + + int m_curProc, m_nProc; }; @@ -117,6 +128,9 @@ private slots: this, SLOT(linkPort(int, bool))); } \endcode + + @note Though called "container", this class does not contain, but only + know the single groups. The inheriting classes are responsible for storage. */ class LinkedModelGroups { diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 16c3f547a09..5f5a130491a 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -37,12 +37,14 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, - bool isLinking, int colNum, int curProc, int nProc, const QString& name) : + LinkedModelGroup *model, int colNum, const QString& name) : QGroupBox(parent), m_colNum(colNum), - m_isLinking(isLinking), + m_isLinking(model->isLinking()), m_grid(new QGridLayout(this)) { + int nProc = model->nProc(); + int curProc = model->curProc(); QString chanName; if(name.isNull()) { From dae999da0bc15f4655d2d5be28f661bf9607f114 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 16:18:08 +0200 Subject: [PATCH 021/120] Code conventions + Merge fixes * Whitespace fixes for LMMS coding conventions * Fix `dcast` now being `dynamicCast` after merge of branch model-visitor --- include/LinkedModelGroups.h | 2 +- src/core/LinkedModelGroups.cpp | 18 +++++------ src/gui/widgets/Controls.cpp | 8 ++--- src/gui/widgets/LinkedModelGroupViews.cpp | 37 +++++++++++------------ 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 7bb0b8a01f1..11568ac3dcf 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -121,7 +121,7 @@ private slots: class: \code - if(multiChannelLinkModel()) { + if (multiChannelLinkModel()) { connect(multiChannelLinkModel(), SIGNAL(dataChanged()), this, SLOT(updateLinkStatesFromGlobal())); connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index ebee7b1f560..f2f9abead6e 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -38,7 +38,7 @@ void LinkedModelGroup::makeLinkingProc() { - for(std::size_t i = 0; i < m_models.size(); ++i) + for (std::size_t i = 0; i < m_models.size(); ++i) { BoolModel* bmo = new BoolModel(true, this, tr("Link channels")); m_linkEnabled.push_back(bmo); @@ -51,7 +51,7 @@ void LinkedModelGroup::makeLinkingProc() void LinkedModelGroup::linkAllModels(bool state) { - for(BoolModel* bmo : m_linkEnabled) { bmo->setValue(state); } + for (BoolModel* bmo : m_linkEnabled) { bmo->setValue(state); } } @@ -85,9 +85,9 @@ void LinkedModelGroup::linkStateChangedSlot() BoolModel* bmo = qobject_cast(sender); Q_ASSERT(bmo); int modelNo = -1, count = 0; - for(BoolModel* bmo2 : m_linkEnabled) + for (BoolModel* bmo2 : m_linkEnabled) { - if(bmo2 == bmo) { modelNo = count; } + if (bmo2 == bmo) { modelNo = count; } ++count; } Q_ASSERT(modelNo >= 0); @@ -127,16 +127,16 @@ void LinkedModelGroups::linkPort(int port, bool state) LinkedModelGroup* first = getGroup(0); LinkedModelGroup* cur; - if(state) + if (state) { - for(std::size_t i = 1; (cur=getGroup(i)); ++i) + for (std::size_t i = 1; (cur=getGroup(i)); ++i) { first->linkControls(cur, port); } } else { - for(std::size_t i = 1; (cur=getGroup(i)); ++i) + for (std::size_t i = 1; (cur=getGroup(i)); ++i) { first->unlinkControls(cur, port); } @@ -155,11 +155,11 @@ void LinkedModelGroups::linkPort(int port, bool state) void LinkedModelGroups::updateLinkStatesFromGlobal() { LinkedModelGroup* first = getGroup(0); - if(m_multiChannelLinkModel->value()) + if (m_multiChannelLinkModel->value()) { first->linkAllModels(true); } - else if( !m_noLink ) + else if (!m_noLink) { first->linkAllModels(false); } diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index cc15b8f6f37..6c68c5ec73c 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -47,7 +47,7 @@ QWidget *KnobControl::topWidget() { return m_knob; } void KnobControl::setModel(AutomatableModel *model) { - m_knob->setModel(model->dcast(true)); + m_knob->setModel(model->dynamicCast(true)); } FloatModel *KnobControl::model() { return m_knob->model(); } @@ -63,7 +63,7 @@ KnobControl::~KnobControl() {} void ComboControl::setText(const QString &text) { m_label->setText(text); } void ComboControl::setModel(AutomatableModel *model) { - m_combo->setModel(model->dcast(true)); } + m_combo->setModel(model->dynamicCast(true)); } ComboBoxModel *ComboControl::model() { return m_combo->model(); } @@ -89,7 +89,7 @@ void CheckControl::setText(const QString &text) { m_label->setText(text); } QWidget *CheckControl::topWidget() { return m_widget; } void CheckControl::setModel(AutomatableModel *model) { - m_checkBox->setModel(model->dcast(true)); } + m_checkBox->setModel(model->dynamicCast(true)); } BoolModel *CheckControl::model() { return m_checkBox->model(); } @@ -113,7 +113,7 @@ void LcdControl::setText(const QString &text) { m_lcd->setLabel(text); } QWidget *LcdControl::topWidget() { return m_lcd; } void LcdControl::setModel(AutomatableModel *model) { - m_lcd->setModel(model->dcast(true)); } + m_lcd->setModel(model->dynamicCast(true)); } IntModel *LcdControl::model() { return m_lcd->model(); } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 5f5a130491a..ccbc57c671e 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -46,7 +46,7 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, int nProc = model->nProc(); int curProc = model->curProc(); QString chanName; - if(name.isNull()) + if (name.isNull()) { switch(nProc) { @@ -59,11 +59,9 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, break; } } - else { - chanName = name; - } + else { chanName = name; } - if(!chanName.isNull()) { setTitle(chanName); } + if (!chanName.isNull()) { setTitle(chanName); } } @@ -81,10 +79,7 @@ void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) std::vector models = group->models(); Q_ASSERT(m_controls.size() == models.size()); - for(AutomatableModel* mdl : models) - { - (*itr++)->setModel(mdl); - } + for (AutomatableModel* mdl : models) { (*itr++)->setModel(mdl); } std::size_t count = 0; for (std::unique_ptr& led : m_leds) @@ -100,12 +95,13 @@ void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) { int colNum2 = m_colNum * (1 + m_isLinking); int wdgNum = static_cast(m_controls.size() * (1 + m_isLinking)); - if(ctrl) + if (ctrl) { int x = wdgNum%colNum2, y = wdgNum/colNum2; // start in row one, add widgets cell by cell - if(m_isLinking) { + if (m_isLinking) + { LedCheckBox* cb = new LedCheckBox(qobject_cast( ctrl->topWidget()->parent())); m_grid->addWidget(cb, y, x); @@ -128,12 +124,12 @@ void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) void LinkedModelGroupViewBase::makeAllGridCellsEqualSized() { int rowHeight = 0, colWidth = 0; - for(int row = 0; row < m_grid->rowCount(); ++row) + for (int row = 0; row < m_grid->rowCount(); ++row) { - for(int col = 0; col < m_grid->columnCount(); ++col) + for (int col = 0; col < m_grid->columnCount(); ++col) { QLayoutItem* layout; - if((layout = m_grid->itemAtPosition(row, col))) + if ((layout = m_grid->itemAtPosition(row, col))) { rowHeight = qMax(rowHeight, layout->geometry().height()); colWidth = qMax(colWidth, layout->geometry().width()); @@ -141,12 +137,12 @@ void LinkedModelGroupViewBase::makeAllGridCellsEqualSized() } } - for(int row = 0; row < m_grid->rowCount(); ++row) + for (int row = 0; row < m_grid->rowCount(); ++row) { m_grid->setRowMinimumHeight(row, rowHeight); } - for(int col = 0; col < m_grid->columnCount(); ++col) + for (int col = 0; col < m_grid->columnCount(); ++col) { m_grid->setColumnMinimumWidth(col, colWidth); } @@ -161,7 +157,7 @@ void LinkedModelGroupViewBase::makeAllGridCellsEqualSized() LinkedModelGroupsViewBase::LinkedModelGroupsViewBase( LinkedModelGroups *ctrlBase) { - if(ctrlBase->multiChannelLinkModel()) + if (ctrlBase->multiChannelLinkModel()) { m_multiChannelLink.reset(new LedCheckBox(QObject::tr("Link Channels"), nullptr)); @@ -173,12 +169,12 @@ LinkedModelGroupsViewBase::LinkedModelGroupsViewBase( void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) { - if(groups->multiChannelLinkModel()) + if (groups->multiChannelLinkModel()) { m_multiChannelLink->setModel(groups->multiChannelLinkModel()); } - for(std::size_t i = 0; getGroupView(i) && groups->getGroup(i); ++i) + for (std::size_t i = 0; getGroupView(i) && groups->getGroup(i); ++i) { getGroupView(i)->modelChanged(groups->getGroup(i)); } @@ -187,6 +183,7 @@ void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) -void LinkedModelGroupsViewBase::MultiChannelLinkDeleter::operator()(LedCheckBox *l) { delete l; } +void LinkedModelGroupsViewBase::MultiChannelLinkDeleter:: + operator()(LedCheckBox *l) { delete l; } From 27f62e664af9e9e56ee5e7f066d648c9c58abafb Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 17:23:02 +0200 Subject: [PATCH 022/120] Do not store nProc in LinkedModelGroup This has been changed because at creation of LinkedModelGroup, the total number of processes may not yet be known. --- include/LinkedModelGroupViews.h | 2 +- include/LinkedModelGroups.h | 5 ++--- src/gui/widgets/LinkedModelGroupViews.cpp | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 88c9b16ec36..c3193e27c75 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -49,7 +49,7 @@ class LinkedModelGroupViewBase : public QGroupBox automatically set if not given */ LinkedModelGroupViewBase(QWidget *parent, class LinkedModelGroup* model, - int colNum, const QString &name = QString()); + int colNum, int nProc, const QString &name = QString()); ~LinkedModelGroupViewBase(); //! Reconnect models if model changed diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 11568ac3dcf..ff38219be8c 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -57,8 +57,8 @@ class LinkedModelGroup : public Model //! @param parent model of the LinkedModelGroups class //! @param curProc number of this processor, counted from 0 //! @param nProc total number of processors - LinkedModelGroup(Model* parent, int curProc, int nProc) : - Model(parent), m_curProc(curProc), m_nProc(nProc) {} + LinkedModelGroup(Model* parent, int curProc) : + Model(parent), m_curProc(curProc) {} //! After all models have been added, make this processor the one which //! will contain link models associated with its controls void makeLinkingProc(); @@ -87,7 +87,6 @@ class LinkedModelGroup : public Model /* General */ - int nProc() const { return m_nProc; } int curProc() const { return m_curProc; } protected: diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index ccbc57c671e..d24f83a93f1 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -37,13 +37,12 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, - LinkedModelGroup *model, int colNum, const QString& name) : + LinkedModelGroup *model, int colNum, int nProc, const QString& name) : QGroupBox(parent), m_colNum(colNum), m_isLinking(model->isLinking()), m_grid(new QGridLayout(this)) { - int nProc = model->nProc(); int curProc = model->curProc(); QString chanName; if (name.isNull()) From b23fd06ef642416dd1e25ebce1147c321f7c7874 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 30 Apr 2019 22:30:09 +0200 Subject: [PATCH 023/120] Improve LinkedModelGroups member funcs --- include/LinkedModelGroups.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index ff38219be8c..e25f71b302c 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -82,7 +82,11 @@ class LinkedModelGroup : public Model */ class BoolModel* linkEnabledModel(std::size_t id) { return m_linkEnabled[id]; } - std::vector models() { return m_models; } + const class BoolModel* linkEnabledModel(std::size_t id) const { + return m_linkEnabled[id]; } + std::vector& models() { return m_models; } + const std::vector& models() const { + return m_models; } /* General @@ -155,6 +159,8 @@ class LinkedModelGroups //! Derived classes must return the group with index @p idx, //! or nullptr if @p is out of range virtual LinkedModelGroup* getGroup(std::size_t idx) = 0; + //! @see getGroup + virtual const LinkedModelGroup* getGroup(std::size_t idx) const = 0; private: //! Model for the "global" linking From c4e341106a390562a36f68e1f9156e4aae2fd92b Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 30 Apr 2019 22:48:16 +0200 Subject: [PATCH 024/120] Remove redundant Lv2Proc::m_controlCount That variable was redundant to the number of models in LinkedModelGroup. --- include/Lv2Proc.h | 3 +-- src/core/lv2/Lv2Proc.cpp | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 2162da6cc9f..de4b09f7061 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -110,7 +110,7 @@ class Lv2Proc : public LinkedModelGroup misc */ class AutomatableModel *modelAtPort(const QString &uri); // unused currently - std::size_t controlCount() const { return m_controlCount; } + std::size_t controlCount() const { return LinkedModelGroup::models().size(); } protected: /* @@ -129,7 +129,6 @@ class Lv2Proc : public LinkedModelGroup std::vector> m_ports; StereoPortRef m_inPorts, m_outPorts; - std::size_t m_controlCount = 0; //! models for the controls, sorted by port symbols std::map m_connectedModels; diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index bf2ae22b701..6cbb5b4178c 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -397,8 +397,6 @@ void Lv2Proc::createPorts() m_proc->m_plugin, ctrl.m_port)), amo); m_proc->addModel(amo); - - ++m_proc->m_controlCount; } } From fa677c25cdb7944c94955a706ec5a29b5cd589d5 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 21:24:05 +0200 Subject: [PATCH 025/120] Only show Lv2 button box if there are buttons --- src/gui/Lv2ViewBase.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 60f0ec27f45..b43b4b190fd 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -130,7 +130,6 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) QGridLayout* grid = new QGridLayout(meAsWidget); QHBoxLayout* btnBox = new QHBoxLayout(); - grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); if (/* DISABLES CODE */ (false)) { m_reloadPluginButton = new QPushButton(QObject::tr("Reload Plugin"), @@ -182,6 +181,11 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) } lilv_nodes_free(props); + if(m_reloadPluginButton || m_toggleUIButton || m_helpButton) + { + grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); + } + int nProcs = static_cast(ctrlBase->controls().size()); Q_ASSERT(m_colNum % nProcs == 0); int colsEach = m_colNum / nProcs; @@ -196,7 +200,9 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) LedCheckBox* led = globalLinkLed(); if (led) + { grid->addWidget(led, Rows::LinkChannelsRow, 0, 1, m_colNum); + } } From 7d91d9b9d68c826c12f904af1e2dcefb4fe5740a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 15 May 2019 23:11:47 +0200 Subject: [PATCH 026/120] LinkedModelGroups: Fix possible OOB read Never access a view if there's no corresponding model --- src/gui/widgets/LinkedModelGroupViews.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index d24f83a93f1..59bc4f69314 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -173,7 +173,7 @@ void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) m_multiChannelLink->setModel(groups->multiChannelLinkModel()); } - for (std::size_t i = 0; getGroupView(i) && groups->getGroup(i); ++i) + for (std::size_t i = 0; groups->getGroup(i) && getGroupView(i); ++i) { getGroupView(i)->modelChanged(groups->getGroup(i)); } From 82cba06690deed14fc2039dc0bf689dea249b8b0 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 15 May 2019 23:15:08 +0200 Subject: [PATCH 027/120] LinkedModelGroups: Hide if no controls --- src/gui/widgets/LinkedModelGroupViews.cpp | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 59bc4f69314..4a69293c173 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -43,24 +43,28 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, m_isLinking(model->isLinking()), m_grid(new QGridLayout(this)) { - int curProc = model->curProc(); - QString chanName; - if (name.isNull()) + if(model->models().size()) { - switch(nProc) + int curProc = model->curProc(); + QString chanName; + if (name.isNull()) { - case 1: break; // don't display any channel name - case 2: - chanName = QObject::tr(curProc ? "Right" : "Left"); - break; - default: - chanName = QObject::tr("Channel ") + QString::number(curProc + 1); - break; + switch(nProc) + { + case 1: break; // don't display any channel name + case 2: + chanName = QObject::tr(curProc ? "Right" : "Left"); + break; + default: + chanName = QObject::tr("Channel ") + QString::number(curProc + 1); + break; + } } - } - else { chanName = name; } + else { chanName = name; } - if (!chanName.isNull()) { setTitle(chanName); } + if (!chanName.isNull()) { setTitle(chanName); } + } + else { setHidden(true); } } From 6c474fedfe4a30b6455069d0437f1d71bdd6392c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 15 May 2019 23:30:57 +0200 Subject: [PATCH 028/120] LinkedModelGroupViews: Fix comment --- include/LinkedModelGroupViews.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index c3193e27c75..210613b86d9 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -85,7 +85,8 @@ class LinkedModelGroupsViewBase LedCheckBox* globalLinkLed() { return m_multiChannelLink.get(); } private: - //! The base class must return the adressed group view + //! The base class must return the adressed group view, or nullptr if index + //! is out of range virtual LinkedModelGroupViewBase* getGroupView(std::size_t idx) = 0; // Implement deletion in the CPP file: From 08c49a3276ec1bc473e3956fbf81b8c660ec9300 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 15 May 2019 23:33:21 +0200 Subject: [PATCH 029/120] LinkedModelGroups: Move model into struct --- include/LinkedModelGroups.h | 19 ++++++++++++++----- src/core/LinkedModelGroups.cpp | 7 ++++--- src/gui/widgets/LinkedModelGroupViews.cpp | 5 +++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index e25f71b302c..9705cd0a332 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -1,4 +1,4 @@ -/* +/* * LinkedModelGroups.h - base classes for groups of linkable models * * Copyright (c) 2019-2019 Johannes Lorenz @@ -80,13 +80,22 @@ class LinkedModelGroup : public Model /* Models */ + struct ModelInfo + { + class AutomatableModel* m_model; + // if you want to hide widgets, or prevent models from being saved, + // add bools here + ModelInfo(class AutomatableModel* m) : m_model(m) + { + } + }; + class BoolModel* linkEnabledModel(std::size_t id) { return m_linkEnabled[id]; } const class BoolModel* linkEnabledModel(std::size_t id) const { return m_linkEnabled[id]; } - std::vector& models() { return m_models; } - const std::vector& models() const { - return m_models; } + std::vector& models() { return m_models; } + const std::vector& models() const { return m_models; } /* General @@ -105,7 +114,7 @@ private slots: //! models for the per-control link-enabled models std::vector m_linkEnabled; //! models for the controls; the vector defines indices for the controls - std::vector m_models; + std::vector m_models; int m_curProc, m_nProc; }; diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index f2f9abead6e..c0ce20b1adc 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -61,7 +61,8 @@ void LinkedModelGroup::linkControls(LinkedModelGroup *other, int id) { Q_ASSERT(id >= 0); std::size_t id2 = static_cast(id); - AutomatableModel::linkModels(m_models[id2], other->m_models[id2]); + AutomatableModel::linkModels( + m_models[id2].m_model, other->m_models[id2].m_model); } @@ -72,7 +73,7 @@ void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, int id) Q_ASSERT(id >= 0); std::size_t id2 = static_cast(id); AutomatableModel::unlinkModels( - m_models[id2], other->m_models[id2]); + m_models[id2].m_model, other->m_models[id2].m_model); } @@ -98,7 +99,7 @@ void LinkedModelGroup::linkStateChangedSlot() void LinkedModelGroup::addModel(AutomatableModel *model) { - m_models.push_back(model); } + m_models.emplace_back(model); } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 4a69293c173..ae94f2973ff 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -78,11 +78,12 @@ LinkedModelGroupViewBase::~LinkedModelGroupViewBase() {} void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) { // reconnect models + using ModelInfo = LinkedModelGroup::ModelInfo; std::vector>::iterator itr = m_controls.begin(); - std::vector models = group->models(); + std::vector models = group->models(); Q_ASSERT(m_controls.size() == models.size()); - for (AutomatableModel* mdl : models) { (*itr++)->setModel(mdl); } + for (const ModelInfo& mdl : models) { (*itr++)->setModel(mdl.m_model); } std::size_t count = 0; for (std::unique_ptr& led : m_leds) From 78dee7d438add5211b6f1872cecfb7099b2c6d77 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 16 May 2019 22:52:44 +0200 Subject: [PATCH 030/120] LinkedModelGroups: Implement load/save --- include/LinkedModelGroups.h | 29 +++++-- src/core/LinkedModelGroups.cpp | 135 ++++++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 9 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 9705cd0a332..9851636d656 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -82,12 +82,10 @@ class LinkedModelGroup : public Model */ struct ModelInfo { + QString m_name; class AutomatableModel* m_model; - // if you want to hide widgets, or prevent models from being saved, - // add bools here - ModelInfo(class AutomatableModel* m) : m_model(m) - { - } + ModelInfo(const QString& name, AutomatableModel* model) + : m_name(name), m_model(model) {} }; class BoolModel* linkEnabledModel(std::size_t id) { @@ -97,14 +95,22 @@ class LinkedModelGroup : public Model std::vector& models() { return m_models; } const std::vector& models() const { return m_models; } + /* + Load/Save + */ + void saveValues(class QDomDocument& doc, class QDomElement& that); + void saveLinksEnabled(QDomDocument &doc, QDomElement &that); + void loadValues(const class QDomElement& that); + void loadLinksEnabled(const class QDomElement &that); + /* General - */ + */ int curProc() const { return m_curProc; } protected: //! Register a further model - void addModel(class AutomatableModel* model); + void addModel(class AutomatableModel* model, const QString& name); private slots: //! Callback called after any of the per-control link-enabled models switch @@ -165,6 +171,15 @@ class LinkedModelGroups //! Callback for the global linking LED void updateLinkStatesFromGlobal(); + /* + Load/Save + */ + void saveSettings(class QDomDocument& doc, class QDomElement& that); + void loadSettings(const class QDomElement& that); + + /* + General + */ //! Derived classes must return the group with index @p idx, //! or nullptr if @p is out of range virtual LinkedModelGroup* getGroup(std::size_t idx) = 0; diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index c0ce20b1adc..2dc7a38c37a 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -24,6 +24,9 @@ #include "LinkedModelGroups.h" +#include +#include + #include "AutomatableModel.h" @@ -79,6 +82,60 @@ void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, int id) +void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) +{ + for(const ModelInfo& minf : m_models) + { + minf.m_model->saveSettings(doc, that, minf.m_name); + } +} + + + + +void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) +{ + if(m_linkEnabled.size()) + { + std::size_t count = 0; + for(BoolModel* bmo : m_linkEnabled) + { + //that.setAttribute(m_models[count++].m_name, bmo->value()); + bmo->saveSettings(doc, that, m_models[count++].m_name); + } + } +} + + + + +void LinkedModelGroup::loadValues(const QDomElement &that) +{ + for(ModelInfo& minf : m_models) + { + // try to load, if it fails, this will load a sane initial value + minf.m_model->loadSettings(that, minf.m_name); + } +} + + + + +void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) +{ + if(m_linkEnabled.size()) + { + std::size_t count = 0; + for(BoolModel* bmo : m_linkEnabled) + { + bmo->loadSettings(that, m_models[count++].m_name); + } + } +} + + + + void LinkedModelGroup::linkStateChangedSlot() { QObject* sender = QObject::sender(); @@ -98,8 +155,10 @@ void LinkedModelGroup::linkStateChangedSlot() -void LinkedModelGroup::addModel(AutomatableModel *model) { - m_models.emplace_back(model); } +void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) +{ + m_models.emplace_back(name, model); +} @@ -173,3 +232,75 @@ void LinkedModelGroups::updateLinkStatesFromGlobal() +void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) +{ + if(getGroup(0)) + { + m_multiChannelLinkModel->saveSettings(doc, that, "link"); + + { + QDomElement links = doc.createElement("links"); + getGroup(0)->saveLinksEnabled(doc, links); + that.appendChild(links); + } + + QDomElement models = doc.createElement("models"); + that.appendChild(models); + + char chanName[] = "chan0"; + for(char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) + { + LinkedModelGroup* lmg = getGroup(static_cast( + *chanPtr - '0')); + if(lmg) + { + QDomElement channel = doc.createElement( + QString::fromUtf8(chanName)); + models.appendChild(channel); + lmg->saveValues(doc, channel); + } + else { + *chanPtr = 0; + } + } + } + else { + // don't even add a "models" node + } +} + + + + +void LinkedModelGroups::loadSettings(const QDomElement& that) +{ + QDomElement models = that.firstChildElement("models"); + if(!models.isNull() && getGroup(0)) + { + m_multiChannelLinkModel->loadSettings(that, "link"); + + { + QDomElement links = that.firstChildElement("links"); + if(!links.isNull()) { getGroup(0)->loadLinksEnabled(links); } + } + + QDomElement lastChan; + char chanName[] = "chan0"; + for(char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) + { + LinkedModelGroup* lmg = getGroup(static_cast( + *chanPtr - '0')); + if(lmg) + { + QDomElement chan = models.firstChildElement(chanName); + if(!chan.isNull()) { lastChan = chan; } + lmg->loadValues(lastChan); + } + else { + *chanPtr = 0; + } + } + } +} + + From 993fa461225db5008f25cfc80cd8598bd178aaa6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 16 May 2019 23:10:24 +0200 Subject: [PATCH 031/120] Coding conventions --- include/LinkedModelGroups.h | 15 ++++++--- src/core/LinkedModelGroups.cpp | 41 ++++++++++------------- src/gui/widgets/Controls.cpp | 18 ++++++---- src/gui/widgets/LinkedModelGroupViews.cpp | 4 +-- 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 9851636d656..a6d03396739 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -88,10 +88,14 @@ class LinkedModelGroup : public Model : m_name(name), m_model(model) {} }; - class BoolModel* linkEnabledModel(std::size_t id) { - return m_linkEnabled[id]; } - const class BoolModel* linkEnabledModel(std::size_t id) const { - return m_linkEnabled[id]; } + class BoolModel* linkEnabledModel(std::size_t id) + { + return m_linkEnabled[id]; + } + const class BoolModel* linkEnabledModel(std::size_t id) const + { + return m_linkEnabled[id]; + } std::vector& models() { return m_models; } const std::vector& models() const { return m_models; } @@ -139,7 +143,8 @@ private slots: class: \code - if (multiChannelLinkModel()) { + if (multiChannelLinkModel()) + { connect(multiChannelLinkModel(), SIGNAL(dataChanged()), this, SLOT(updateLinkStatesFromGlobal())); connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 2dc7a38c37a..3753e6ecf68 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -84,7 +84,7 @@ void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, int id) void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) { - for(const ModelInfo& minf : m_models) + for (const ModelInfo& minf : m_models) { minf.m_model->saveSettings(doc, that, minf.m_name); } @@ -95,10 +95,10 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) { - if(m_linkEnabled.size()) + if (m_linkEnabled.size()) { std::size_t count = 0; - for(BoolModel* bmo : m_linkEnabled) + for (BoolModel* bmo : m_linkEnabled) { //that.setAttribute(m_models[count++].m_name, bmo->value()); bmo->saveSettings(doc, that, m_models[count++].m_name); @@ -111,7 +111,7 @@ void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) void LinkedModelGroup::loadValues(const QDomElement &that) { - for(ModelInfo& minf : m_models) + for (ModelInfo& minf : m_models) { // try to load, if it fails, this will load a sane initial value minf.m_model->loadSettings(that, minf.m_name); @@ -123,10 +123,10 @@ void LinkedModelGroup::loadValues(const QDomElement &that) void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) { - if(m_linkEnabled.size()) + if (m_linkEnabled.size()) { std::size_t count = 0; - for(BoolModel* bmo : m_linkEnabled) + for (BoolModel* bmo : m_linkEnabled) { bmo->loadSettings(that, m_models[count++].m_name); } @@ -175,7 +175,8 @@ LinkedModelGroups::~LinkedModelGroups() {} -void LinkedModelGroups::createMultiChannelLinkModel() { +void LinkedModelGroups::createMultiChannelLinkModel() +{ m_multiChannelLinkModel.reset(new BoolModel(true, nullptr)); } @@ -234,7 +235,7 @@ void LinkedModelGroups::updateLinkStatesFromGlobal() void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) { - if(getGroup(0)) + if (getGroup(0)) { m_multiChannelLinkModel->saveSettings(doc, that, "link"); @@ -248,25 +249,21 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) that.appendChild(models); char chanName[] = "chan0"; - for(char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) + for (char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) { LinkedModelGroup* lmg = getGroup(static_cast( *chanPtr - '0')); - if(lmg) + if (lmg) { QDomElement channel = doc.createElement( QString::fromUtf8(chanName)); models.appendChild(channel); lmg->saveValues(doc, channel); } - else { - *chanPtr = 0; - } + else { *chanPtr = 0; } } } - else { - // don't even add a "models" node - } + else { /* don't even add a "models" node */ } } @@ -275,7 +272,7 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) void LinkedModelGroups::loadSettings(const QDomElement& that) { QDomElement models = that.firstChildElement("models"); - if(!models.isNull() && getGroup(0)) + if (!models.isNull() && getGroup(0)) { m_multiChannelLinkModel->loadSettings(that, "link"); @@ -286,19 +283,17 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) QDomElement lastChan; char chanName[] = "chan0"; - for(char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) + for (char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) { LinkedModelGroup* lmg = getGroup(static_cast( *chanPtr - '0')); - if(lmg) + if (lmg) { QDomElement chan = models.firstChildElement(chanName); - if(!chan.isNull()) { lastChan = chan; } + if (!chan.isNull()) { lastChan = chan; } lmg->loadValues(lastChan); } - else { - *chanPtr = 0; - } + else { *chanPtr = 0; } } } } diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index 6c68c5ec73c..1263e82c318 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -62,8 +62,10 @@ KnobControl::~KnobControl() {} void ComboControl::setText(const QString &text) { m_label->setText(text); } -void ComboControl::setModel(AutomatableModel *model) { - m_combo->setModel(model->dynamicCast(true)); } +void ComboControl::setModel(AutomatableModel *model) +{ + m_combo->setModel(model->dynamicCast(true)); +} ComboBoxModel *ComboControl::model() { return m_combo->model(); } @@ -88,8 +90,10 @@ void CheckControl::setText(const QString &text) { m_label->setText(text); } QWidget *CheckControl::topWidget() { return m_widget; } -void CheckControl::setModel(AutomatableModel *model) { - m_checkBox->setModel(model->dynamicCast(true)); } +void CheckControl::setModel(AutomatableModel *model) +{ + m_checkBox->setModel(model->dynamicCast(true)); +} BoolModel *CheckControl::model() { return m_checkBox->model(); } @@ -112,8 +116,10 @@ void LcdControl::setText(const QString &text) { m_lcd->setLabel(text); } QWidget *LcdControl::topWidget() { return m_lcd; } -void LcdControl::setModel(AutomatableModel *model) { - m_lcd->setModel(model->dynamicCast(true)); } +void LcdControl::setModel(AutomatableModel *model) +{ + m_lcd->setModel(model->dynamicCast(true)); +} IntModel *LcdControl::model() { return m_lcd->model(); } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index ae94f2973ff..08e74e56a40 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -43,13 +43,13 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, m_isLinking(model->isLinking()), m_grid(new QGridLayout(this)) { - if(model->models().size()) + if (model->models().size()) { int curProc = model->curProc(); QString chanName; if (name.isNull()) { - switch(nProc) + switch (nProc) { case 1: break; // don't display any channel name case 2: From 3e0525d579ac27216742eae58c4483bca89a7b55 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 25 May 2019 18:25:45 +0200 Subject: [PATCH 032/120] LinkedModelGroups: Fix nullptr indirection --- src/core/LinkedModelGroups.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 3753e6ecf68..6be27b23508 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -237,7 +237,10 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) { if (getGroup(0)) { - m_multiChannelLinkModel->saveSettings(doc, that, "link"); + if (m_multiChannelLinkModel) + { + m_multiChannelLinkModel->saveSettings(doc, that, "link"); + } { QDomElement links = doc.createElement("links"); @@ -274,7 +277,10 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) QDomElement models = that.firstChildElement("models"); if (!models.isNull() && getGroup(0)) { - m_multiChannelLinkModel->loadSettings(that, "link"); + if (m_multiChannelLinkModel) + { + m_multiChannelLinkModel->loadSettings(that, "link"); + } { QDomElement links = that.firstChildElement("links"); From e2d1320496253c85623ec3897a1997745aca6c74 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 26 May 2019 23:29:54 +0200 Subject: [PATCH 033/120] Lv2: Fix compile after previous merge --- include/Lv2Ports.h | 1 + src/core/lv2/Lv2Ports.cpp | 8 ++++++++ src/core/lv2/Lv2Proc.cpp | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 5c1341b00ff..64aa322e060 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -121,6 +121,7 @@ struct PortBase : public Meta virtual void accept(ConstVisitor& v) const = 0; QString name() const; + QString uri() const; virtual ~PortBase(); }; diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index c5a454f2ef4..1b78846a985 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -192,6 +192,14 @@ QString PortBase::name() const +QString PortBase::uri() const +{ + return lilv_node_as_string(lilv_port_get_symbol(m_plugin, m_port)); +} + + + + Audio::Audio(std::size_t bufferSize, bool isSidechain) : m_buffer(bufferSize), m_sidechain(isSidechain) { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 6cbb5b4178c..75f9f108409 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -396,7 +396,7 @@ void Lv2Proc::createPorts() lilv_node_as_string(lilv_port_get_symbol( m_proc->m_plugin, ctrl.m_port)), amo); - m_proc->addModel(amo); + m_proc->addModel(amo, ctrl.uri()); } } From 6c90e5084d52b5edca3dc4aa3be8b025e323de3e Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 26 May 2019 23:30:18 +0200 Subject: [PATCH 034/120] Lv2ControlBase: Enable load/save --- src/core/lv2/Lv2ControlBase.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index b5d0ec2593d..706ab9bda35 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -156,9 +156,9 @@ void Lv2ControlBase::run(unsigned frames) { void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) { - // TODO - (void)doc; - (void)that; + LinkedModelGroups::saveSettings(doc, that); + + // TODO: save state if supported by plugin } @@ -166,7 +166,9 @@ void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) void Lv2ControlBase::loadSettings(const QDomElement &that) { - (void)that; + LinkedModelGroups::loadSettings(that); + + // TODO: load state if supported by plugin } From 029c2e40612ca8b5161073eb721a08d3d81013f4 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 26 May 2019 23:33:57 +0200 Subject: [PATCH 035/120] Remove unused variable --- include/LinkedModelGroups.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index a6d03396739..c3ec36d632e 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -126,7 +126,7 @@ private slots: //! models for the controls; the vector defines indices for the controls std::vector m_models; - int m_curProc, m_nProc; + int m_curProc; }; From 2edb35e986b35d7cc339f8dbf6e23ce9310e5c38 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 27 May 2019 19:06:29 +0200 Subject: [PATCH 036/120] LinkedModelGroups: Don't save linked models twice --- include/LinkedModelGroups.h | 9 +++-- src/core/LinkedModelGroups.cpp | 73 +++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index c3ec36d632e..7223c4c0799 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -75,7 +75,7 @@ class LinkedModelGroup : public Model //! @see linkControls void unlinkControls(LinkedModelGroup *other, int id); //! Return whether this is the first of more than one processors - bool isLinking() { return m_linkEnabled.size(); } + bool isLinking() const { return m_linkEnabled.size(); } /* Models @@ -102,9 +102,12 @@ class LinkedModelGroup : public Model /* Load/Save */ - void saveValues(class QDomDocument& doc, class QDomElement& that); + //! @param lmg0 The linking model group with index 0 + void saveValues(class QDomDocument& doc, class QDomElement& that, + const LinkedModelGroup *lmg0); void saveLinksEnabled(QDomDocument &doc, QDomElement &that); - void loadValues(const class QDomElement& that); + //! @param lmg0 The linking model group with index 0 + void loadValues(const class QDomElement& that, const LinkedModelGroup *lmg0); void loadLinksEnabled(const class QDomElement &that); /* diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 6be27b23508..7b201dc0331 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -82,11 +82,33 @@ void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, int id) -void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) +void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, + const LinkedModelGroup *lmg0) { + Q_ASSERT(lmg0->isLinking()); for (const ModelInfo& minf : m_models) { - minf.m_model->saveSettings(doc, that, minf.m_name); + std::size_t idx = 0; + if (this == lmg0) { idx = lmg0->models().size(); } // force saving + else + { + for (; idx < lmg0->models().size(); ++idx) + { + if (lmg0->models()[idx].m_name == minf.m_name) + { + break; + } + } + } + if (idx < lmg0->models().size() && + lmg0->linkEnabledModel(idx)->value()) + { + // link is enabled => nothing to save + } + else + { + minf.m_model->saveSettings(doc, that, minf.m_name); + } } } @@ -109,12 +131,33 @@ void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) -void LinkedModelGroup::loadValues(const QDomElement &that) +void LinkedModelGroup::loadValues(const QDomElement &that, + const LinkedModelGroup* lmg0) { for (ModelInfo& minf : m_models) { - // try to load, if it fails, this will load a sane initial value - minf.m_model->loadSettings(that, minf.m_name); + std::size_t idx = 0; + if (this == lmg0) { idx = lmg0->models().size(); } // force loading + else + { + for (; idx < lmg0->models().size(); ++idx) + { + if (lmg0->models()[idx].m_name == minf.m_name) + { + break; + } + } + } + if (idx < lmg0->models().size() && + lmg0->linkEnabledModel(idx)->value()) + { + // link is enabled => it will load automatically + } + else + { + // try to load, if it fails, this will load a sane initial value + minf.m_model->loadSettings(that, minf.m_name); + } } } @@ -237,11 +280,14 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) { if (getGroup(0)) { + bool allLinked = false; if (m_multiChannelLinkModel) { m_multiChannelLinkModel->saveSettings(doc, that, "link"); + allLinked = m_multiChannelLinkModel->value(); } + if(!allLinked && getGroup(1)) { QDomElement links = doc.createElement("links"); getGroup(0)->saveLinksEnabled(doc, links); @@ -261,9 +307,12 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) QDomElement channel = doc.createElement( QString::fromUtf8(chanName)); models.appendChild(channel); - lmg->saveValues(doc, channel); + lmg->saveValues(doc, channel, getGroup(0)); } - else { *chanPtr = 0; } + else { *chanPtr = 0; } // end reached + + // if all models are linked, stop after first group + if (allLinked) { *chanPtr = 0; } } } else { /* don't even add a "models" node */ } @@ -277,11 +326,14 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) QDomElement models = that.firstChildElement("models"); if (!models.isNull() && getGroup(0)) { + bool allLinked = false; if (m_multiChannelLinkModel) { m_multiChannelLinkModel->loadSettings(that, "link"); + allLinked = m_multiChannelLinkModel->value(); } + if (!allLinked && getGroup(1)) { QDomElement links = that.firstChildElement("links"); if(!links.isNull()) { getGroup(0)->loadLinksEnabled(links); } @@ -297,9 +349,12 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) { QDomElement chan = models.firstChildElement(chanName); if (!chan.isNull()) { lastChan = chan; } - lmg->loadValues(lastChan); + lmg->loadValues(lastChan, getGroup(0)); } - else { *chanPtr = 0; } + else { *chanPtr = 0; } // end reached + + // if all models are linked, stop after first group + if (allLinked) { *chanPtr = 0; } } } } From 24ca9f189c65b0c1c796387a3c905530fa4c0452 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 30 May 2019 18:04:23 +0200 Subject: [PATCH 037/120] Code review Primarily remove `Base` postfix from class names --- include/Controls.h | 12 ++++---- include/LinkedModelGroupViews.h | 37 +++++++++++++++-------- src/core/LinkedModelGroups.cpp | 3 -- src/gui/widgets/Controls.cpp | 2 +- src/gui/widgets/LinkedModelGroupViews.cpp | 20 ++++++------ 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/include/Controls.h b/include/Controls.h index e64601bc694..e00649126c7 100644 --- a/include/Controls.h +++ b/include/Controls.h @@ -45,7 +45,7 @@ class AutomatableModel; (justification: setting the wrong typed model to a widget will cause hard-to-find runtime errors) */ -class ControlBase +class Control { public: virtual QWidget* topWidget() = 0; @@ -54,11 +54,11 @@ class ControlBase virtual void setModel(AutomatableModel* model) = 0; virtual AutomatableModel* model() = 0; - virtual ~ControlBase(); + virtual ~Control(); }; -class KnobControl : public ControlBase +class KnobControl : public Control { class Knob* m_knob; @@ -74,7 +74,7 @@ class KnobControl : public ControlBase }; -class ComboControl : public ControlBase +class ComboControl : public Control { QWidget* m_widget; class ComboBox* m_combo; @@ -92,7 +92,7 @@ class ComboControl : public ControlBase }; -class LcdControl : public ControlBase +class LcdControl : public Control { class LcdSpinBox* m_lcd; @@ -108,7 +108,7 @@ class LcdControl : public ControlBase }; -class CheckControl : public ControlBase +class CheckControl : public Control { QWidget* m_widget; class LedCheckBox* m_checkBox; diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 210613b86d9..a9545758770 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -37,9 +37,15 @@ */ -//! View for one processor, LinkedModelGroupsViewBase contains 2 -//! of those for mono plugins -class LinkedModelGroupViewBase : public QGroupBox +/** + View for one processor, LinkedModelGroupsViewBase contains 2 + of those for mono plugins. + + @note This class, and no inheriting classes, shall inherit ModelView. + The "view" in the name is just for consistency + with LinkedModelGroupsView. +*/ +class LinkedModelGroupView : public QGroupBox { public: /** @@ -48,16 +54,16 @@ class LinkedModelGroupViewBase : public QGroupBox @param name Name for the group, like "Left" or "Group 1", automatically set if not given */ - LinkedModelGroupViewBase(QWidget *parent, class LinkedModelGroup* model, + LinkedModelGroupView(QWidget *parent, class LinkedModelGroup* model, int colNum, int nProc, const QString &name = QString()); - ~LinkedModelGroupViewBase(); + ~LinkedModelGroupView(); //! Reconnect models if model changed void modelChanged(class LinkedModelGroup *linkedModelGroup); protected: //! Add a control to this widget - void addControl(class ControlBase *ctrl); + void addControl(class Control *ctrl); private: void makeAllGridCellsEqualSized(); @@ -65,18 +71,23 @@ class LinkedModelGroupViewBase : public QGroupBox int m_colNum; //!< column number in surrounding grid in Lv2ViewBase bool m_isLinking; class QGridLayout* m_grid; - std::vector> m_controls; + std::vector> m_controls; std::vector> m_leds; }; -//! Base class for view for one plugin with linkable models. -//! Provides a global channel link LED. -class LinkedModelGroupsViewBase +/** + Base class for view for one plugin with linkable models. + Provides a global channel link LED. + + @note It's intended this class does not inherit from ModelView. + Inheriting classes need to do that, see Lv2Instrument.h +*/ +class LinkedModelGroupsView { protected: - LinkedModelGroupsViewBase(class LinkedModelGroups *ctrlBase); - ~LinkedModelGroupsViewBase() = default; + LinkedModelGroupsView(class LinkedModelGroups *ctrlBase); + ~LinkedModelGroupsView() = default; //! Reconnect models if model changed; to be called by child virtuals void modelChanged(class LinkedModelGroups* ctrlBase); @@ -87,7 +98,7 @@ class LinkedModelGroupsViewBase private: //! The base class must return the adressed group view, or nullptr if index //! is out of range - virtual LinkedModelGroupViewBase* getGroupView(std::size_t idx) = 0; + virtual LinkedModelGroupView* getGroupView(std::size_t idx) = 0; // Implement deletion in the CPP file: struct MultiChannelLinkDeleter { void operator()(LedCheckBox* l); }; diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 7b201dc0331..f28e6e33547 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -122,7 +122,6 @@ void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) std::size_t count = 0; for (BoolModel* bmo : m_linkEnabled) { - //that.setAttribute(m_models[count++].m_name, bmo->value()); bmo->saveSettings(doc, that, m_models[count++].m_name); } } @@ -268,8 +267,6 @@ void LinkedModelGroups::updateLinkStatesFromGlobal() first->linkAllModels(false); } - // if global channel link state has changed, always ignore link - // status of individual ports in the future m_noLink = false; } diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index 1263e82c318..b5844706028 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -36,7 +36,7 @@ -ControlBase::~ControlBase() {} +Control::~Control() {} diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 08e74e56a40..21a4ef9bc67 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -36,7 +36,7 @@ */ -LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, +LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, LinkedModelGroup *model, int colNum, int nProc, const QString& name) : QGroupBox(parent), m_colNum(colNum), @@ -70,16 +70,16 @@ LinkedModelGroupViewBase::LinkedModelGroupViewBase(QWidget* parent, -LinkedModelGroupViewBase::~LinkedModelGroupViewBase() {} +LinkedModelGroupView::~LinkedModelGroupView() {} -void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) +void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) { // reconnect models using ModelInfo = LinkedModelGroup::ModelInfo; - std::vector>::iterator itr = m_controls.begin(); + std::vector>::iterator itr = m_controls.begin(); std::vector models = group->models(); Q_ASSERT(m_controls.size() == models.size()); @@ -95,7 +95,7 @@ void LinkedModelGroupViewBase::modelChanged(LinkedModelGroup *group) -void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) +void LinkedModelGroupView::addControl(Control* ctrl) { int colNum2 = m_colNum * (1 + m_isLinking); int wdgNum = static_cast(m_controls.size() * (1 + m_isLinking)); @@ -112,7 +112,7 @@ void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) m_leds.push_back(std::unique_ptr(cb)); } - m_controls.push_back(std::unique_ptr(ctrl)); + m_controls.push_back(std::unique_ptr(ctrl)); m_grid->addWidget(ctrl->topWidget(), y, x + 1, Qt::AlignCenter); wdgNum += m_isLinking; ++wdgNum; @@ -125,7 +125,7 @@ void LinkedModelGroupViewBase::addControl(ControlBase* ctrl) -void LinkedModelGroupViewBase::makeAllGridCellsEqualSized() +void LinkedModelGroupView::makeAllGridCellsEqualSized() { int rowHeight = 0, colWidth = 0; for (int row = 0; row < m_grid->rowCount(); ++row) @@ -158,7 +158,7 @@ void LinkedModelGroupViewBase::makeAllGridCellsEqualSized() */ -LinkedModelGroupsViewBase::LinkedModelGroupsViewBase( +LinkedModelGroupsView::LinkedModelGroupsView( LinkedModelGroups *ctrlBase) { if (ctrlBase->multiChannelLinkModel()) @@ -171,7 +171,7 @@ LinkedModelGroupsViewBase::LinkedModelGroupsViewBase( -void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) +void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) { if (groups->multiChannelLinkModel()) { @@ -187,7 +187,7 @@ void LinkedModelGroupsViewBase::modelChanged(LinkedModelGroups *groups) -void LinkedModelGroupsViewBase::MultiChannelLinkDeleter:: +void LinkedModelGroupsView::MultiChannelLinkDeleter:: operator()(LedCheckBox *l) { delete l; } From 0c1758edd1193d763950ebd62cfa155eddd712bf Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 13 Jun 2019 22:41:05 +0200 Subject: [PATCH 038/120] Review rework, thanks to @DomClark --- include/LinkedModelGroupViews.h | 12 +- include/LinkedModelGroups.h | 32 ++-- include/stdshims.h | 7 + src/core/LinkedModelGroups.cpp | 171 ++++++++-------------- src/gui/widgets/LinkedModelGroupViews.cpp | 42 ++++-- 5 files changed, 112 insertions(+), 152 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index a9545758770..7e7c8f3e920 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -26,6 +26,7 @@ #define LINKEDMODELGROUPVIEWS_H +#include #include #include #include @@ -41,8 +42,8 @@ View for one processor, LinkedModelGroupsViewBase contains 2 of those for mono plugins. - @note This class, and no inheriting classes, shall inherit ModelView. - The "view" in the name is just for consistency + @note Neither this class, nor any inheriting classes, shall inherit + ModelView. The "view" in the name is just for consistency with LinkedModelGroupsView. */ class LinkedModelGroupView : public QGroupBox @@ -55,7 +56,7 @@ class LinkedModelGroupView : public QGroupBox automatically set if not given */ LinkedModelGroupView(QWidget *parent, class LinkedModelGroup* model, - int colNum, int nProc, const QString &name = QString()); + std::size_t colNum, std::size_t nProc, const QString &name = QString()); ~LinkedModelGroupView(); //! Reconnect models if model changed @@ -68,7 +69,8 @@ class LinkedModelGroupView : public QGroupBox private: void makeAllGridCellsEqualSized(); - int m_colNum; //!< column number in surrounding grid in Lv2ViewBase + //! column number in surrounding grid in LinkedModelGroupsView + std::size_t m_colNum; bool m_isLinking; class QGridLayout* m_grid; std::vector> m_controls; @@ -81,7 +83,7 @@ class LinkedModelGroupView : public QGroupBox Provides a global channel link LED. @note It's intended this class does not inherit from ModelView. - Inheriting classes need to do that, see Lv2Instrument.h + Inheriting classes need to do that, see e.g. Lv2Instrument.h */ class LinkedModelGroupsView { diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 7223c4c0799..d78ce0ec6f6 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -26,6 +26,7 @@ #define LINKEDMODELGROUPS_H +#include #include #include @@ -48,7 +49,7 @@ class LinkedModelGroup : public Model Q_OBJECT signals: //! Signal emitted after any of the per-control link-enabled models switch - void linkStateChanged(int id, bool value); + void linkStateChanged(std::size_t id, bool value); public: /* @@ -57,7 +58,7 @@ class LinkedModelGroup : public Model //! @param parent model of the LinkedModelGroups class //! @param curProc number of this processor, counted from 0 //! @param nProc total number of processors - LinkedModelGroup(Model* parent, int curProc) : + LinkedModelGroup(Model* parent, std::size_t curProc) : Model(parent), m_curProc(curProc) {} //! After all models have been added, make this processor the one which //! will contain link models associated with its controls @@ -67,13 +68,13 @@ class LinkedModelGroup : public Model Linking */ //! Set all per-control link-enabled models to @p state, which will - //! also link or unlink them (via `Lv2ControlBase::linkPort()`) + //! also link or unlink them (via `LinkedModelGroups::linkModel()`) void linkAllModels(bool state); //! Link specified port with the associated port of @p other //! @param id id of the port, conforming to m_models - void linkControls(LinkedModelGroup* other, int id); + void linkControls(LinkedModelGroup* other, std::size_t id); //! @see linkControls - void unlinkControls(LinkedModelGroup *other, int id); + void unlinkControls(LinkedModelGroup *other, std::size_t id); //! Return whether this is the first of more than one processors bool isLinking() const { return m_linkEnabled.size(); } @@ -113,23 +114,19 @@ class LinkedModelGroup : public Model /* General */ - int curProc() const { return m_curProc; } + std::size_t curProc() const { return m_curProc; } protected: //! Register a further model void addModel(class AutomatableModel* model, const QString& name); -private slots: - //! Callback called after any of the per-control link-enabled models switch - void linkStateChangedSlot(); - private: //! models for the per-control link-enabled models std::vector m_linkEnabled; //! models for the controls; the vector defines indices for the controls std::vector m_models; - int m_curProc; + std::size_t m_curProc; }; @@ -150,8 +147,8 @@ private slots: { connect(multiChannelLinkModel(), SIGNAL(dataChanged()), this, SLOT(updateLinkStatesFromGlobal())); - connect(getGroup(0), SIGNAL(linkStateChanged(int, bool)), - this, SLOT(linkPort(int, bool))); + connect(getGroup(0), SIGNAL(linkStateChanged(std::size_t, bool)), + this, SLOT(linkPort(std::size_t, bool))); } \endcode @@ -171,11 +168,12 @@ class LinkedModelGroups /* to be called by slots */ - //! Take a specified port from the first Lv2Proc and link or unlink it - //! from the associated port of every other Lv2Proc - //! @param port number conforming to Lv2Proc::m_modelVector + //! Take a specified model from the first LinkedModelGroup + //! and link or unlink it to/from the associated model + //! of every other LinkedModelGroup + //! @param model number conforming to getGroup() //! @param state True iff it should be linked - void linkPort(int port, bool state); + void linkModel(std::size_t model, bool state); //! Callback for the global linking LED void updateLinkStatesFromGlobal(); diff --git a/include/stdshims.h b/include/stdshims.h index 85c4f457aab..5eee6543cac 100644 --- a/include/stdshims.h +++ b/include/stdshims.h @@ -21,6 +21,13 @@ std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } + +//! Overload for the case a deleter should be specified +template +std::unique_ptr make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} #endif #endif // include guard diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index f28e6e33547..2029c15d374 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -28,6 +28,7 @@ #include #include "AutomatableModel.h" +#include "stdshims.h" @@ -45,7 +46,8 @@ void LinkedModelGroup::makeLinkingProc() { BoolModel* bmo = new BoolModel(true, this, tr("Link channels")); m_linkEnabled.push_back(bmo); - connect(bmo, SIGNAL(dataChanged()), this, SLOT(linkStateChangedSlot())); + connect(bmo, &BoolModel::dataChanged, this, + [this, bmo, i]() { emit linkStateChanged(i, bmo->value()); }); } } @@ -60,23 +62,19 @@ void LinkedModelGroup::linkAllModels(bool state) -void LinkedModelGroup::linkControls(LinkedModelGroup *other, int id) +void LinkedModelGroup::linkControls(LinkedModelGroup *other, std::size_t id) { - Q_ASSERT(id >= 0); - std::size_t id2 = static_cast(id); AutomatableModel::linkModels( - m_models[id2].m_model, other->m_models[id2].m_model); + m_models[id].m_model, other->m_models[id].m_model); } -void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, int id) +void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, std::size_t id) { - Q_ASSERT(id >= 0); - std::size_t id2 = static_cast(id); AutomatableModel::unlinkModels( - m_models[id2].m_model, other->m_models[id2].m_model); + m_models[id].m_model, other->m_models[id].m_model); } @@ -86,28 +84,16 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, const LinkedModelGroup *lmg0) { Q_ASSERT(lmg0->isLinking()); - for (const ModelInfo& minf : m_models) + for (std::size_t idx = 0; idx < m_models.size(); ++idx) { - std::size_t idx = 0; - if (this == lmg0) { idx = lmg0->models().size(); } // force saving - else - { - for (; idx < lmg0->models().size(); ++idx) - { - if (lmg0->models()[idx].m_name == minf.m_name) - { - break; - } - } - } - if (idx < lmg0->models().size() && - lmg0->linkEnabledModel(idx)->value()) + if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) { - // link is enabled => nothing to save + // try to load, if it fails, this will load a sane initial value + m_models[idx].m_model->saveSettings(doc, that, m_models[idx].m_name); } else { - minf.m_model->saveSettings(doc, that, minf.m_name); + // model has the same value as in the first LinkedModelGroup } } } @@ -117,13 +103,9 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) { - if (m_linkEnabled.size()) + for (std::size_t i = 0; i < m_linkEnabled.size(); ++i) { - std::size_t count = 0; - for (BoolModel* bmo : m_linkEnabled) - { - bmo->saveSettings(doc, that, m_models[count++].m_name); - } + m_linkEnabled[i]->saveSettings(doc, that, m_models[i].m_name); } } @@ -133,29 +115,16 @@ void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) void LinkedModelGroup::loadValues(const QDomElement &that, const LinkedModelGroup* lmg0) { - for (ModelInfo& minf : m_models) + for (std::size_t idx = 0; idx < m_models.size(); ++idx) { - std::size_t idx = 0; - if (this == lmg0) { idx = lmg0->models().size(); } // force loading - else - { - for (; idx < lmg0->models().size(); ++idx) - { - if (lmg0->models()[idx].m_name == minf.m_name) - { - break; - } - } - } - if (idx < lmg0->models().size() && - lmg0->linkEnabledModel(idx)->value()) + if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) { - // link is enabled => it will load automatically + // try to load, if it fails, this will load a sane initial value + m_models[idx].m_model->loadSettings(that, m_models[idx].m_name); } else { - // try to load, if it fails, this will load a sane initial value - minf.m_model->loadSettings(that, minf.m_name); + // model has the same value as in the first LinkedModelGroup } } } @@ -165,38 +134,15 @@ void LinkedModelGroup::loadValues(const QDomElement &that, void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) { - if (m_linkEnabled.size()) + for (std::size_t i = 0; i < m_linkEnabled.size(); ++i) { - std::size_t count = 0; - for (BoolModel* bmo : m_linkEnabled) - { - bmo->loadSettings(that, m_models[count++].m_name); - } + m_linkEnabled[i]->loadSettings(that, m_models[i].m_name); } } -void LinkedModelGroup::linkStateChangedSlot() -{ - QObject* sender = QObject::sender(); - Q_ASSERT(sender); - BoolModel* bmo = qobject_cast(sender); - Q_ASSERT(bmo); - int modelNo = -1, count = 0; - for (BoolModel* bmo2 : m_linkEnabled) - { - if (bmo2 == bmo) { modelNo = count; } - ++count; - } - Q_ASSERT(modelNo >= 0); - emit linkStateChanged(modelNo, bmo->value()); -} - - - - void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) { m_models.emplace_back(name, model); @@ -219,34 +165,34 @@ LinkedModelGroups::~LinkedModelGroups() {} void LinkedModelGroups::createMultiChannelLinkModel() { - m_multiChannelLinkModel.reset(new BoolModel(true, nullptr)); + m_multiChannelLinkModel = make_unique(true, nullptr); } -void LinkedModelGroups::linkPort(int port, bool state) +void LinkedModelGroups::linkModel(std::size_t model, bool state) { LinkedModelGroup* first = getGroup(0); LinkedModelGroup* cur; if (state) { - for (std::size_t i = 1; (cur=getGroup(i)); ++i) + for (std::size_t i = 1; (cur = getGroup(i)); ++i) { - first->linkControls(cur, port); + first->linkControls(cur, model); } } else { - for (std::size_t i = 1; (cur=getGroup(i)); ++i) + for (std::size_t i = 1; (cur = getGroup(i)); ++i) { - first->unlinkControls(cur, port); + first->unlinkControls(cur, model); } // m_multiChannelLinkModel.setValue() will call // updateLinkStatesFromGlobal()... - // m_noLink will make sure that this will not unlink any other ports + // m_noLink will make sure that this will not unlink any other models m_noLink = true; m_multiChannelLinkModel->setValue( false ); } @@ -275,7 +221,8 @@ void LinkedModelGroups::updateLinkStatesFromGlobal() void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) { - if (getGroup(0)) + LinkedModelGroup* grp0 = getGroup(0); + if (grp0) { bool allLinked = false; if (m_multiChannelLinkModel) @@ -284,10 +231,10 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) allLinked = m_multiChannelLinkModel->value(); } - if(!allLinked && getGroup(1)) + if (!allLinked && getGroup(1)) { QDomElement links = doc.createElement("links"); - getGroup(0)->saveLinksEnabled(doc, links); + grp0->saveLinksEnabled(doc, links); that.appendChild(links); } @@ -295,21 +242,18 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) that.appendChild(models); char chanName[] = "chan0"; - for (char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) + LinkedModelGroup* lmg; + for (std::size_t chanIdx = 0; + // stop after last group + // if all models are linked, store only the first group + (lmg = getGroup(chanIdx)) && !(allLinked && chanIdx > 0); + ++chanIdx) { - LinkedModelGroup* lmg = getGroup(static_cast( - *chanPtr - '0')); - if (lmg) - { - QDomElement channel = doc.createElement( - QString::fromUtf8(chanName)); - models.appendChild(channel); - lmg->saveValues(doc, channel, getGroup(0)); - } - else { *chanPtr = 0; } // end reached - - // if all models are linked, stop after first group - if (allLinked) { *chanPtr = 0; } + chanName[4] = '0' + static_cast(chanIdx); + QDomElement channel = doc.createElement( + QString::fromUtf8(chanName)); + models.appendChild(channel); + lmg->saveValues(doc, channel, grp0); } } else { /* don't even add a "models" node */ } @@ -321,7 +265,8 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) void LinkedModelGroups::loadSettings(const QDomElement& that) { QDomElement models = that.firstChildElement("models"); - if (!models.isNull() && getGroup(0)) + LinkedModelGroup* grp0; + if (!models.isNull() && (grp0 = getGroup(0))) { bool allLinked = false; if (m_multiChannelLinkModel) @@ -333,25 +278,23 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) if (!allLinked && getGroup(1)) { QDomElement links = that.firstChildElement("links"); - if(!links.isNull()) { getGroup(0)->loadLinksEnabled(links); } + if (!links.isNull()) { grp0->loadLinksEnabled(links); } } QDomElement lastChan; char chanName[] = "chan0"; - for (char* chanPtr = chanName + 4; *chanPtr >= '0'; ++*chanPtr) + LinkedModelGroup* lmg; + for (std::size_t chanIdx = 0; + // stop after last group + // if all models are linked, read only the first group + (lmg = getGroup(chanIdx)) && !(allLinked && chanIdx > 0); + ++chanIdx) { - LinkedModelGroup* lmg = getGroup(static_cast( - *chanPtr - '0')); - if (lmg) - { - QDomElement chan = models.firstChildElement(chanName); - if (!chan.isNull()) { lastChan = chan; } - lmg->loadValues(lastChan, getGroup(0)); - } - else { *chanPtr = 0; } // end reached - - // if all models are linked, stop after first group - if (allLinked) { *chanPtr = 0; } + chanName[4] = '0' + static_cast(chanIdx); + QDomElement chan = models.firstChildElement(chanName); + if (!chan.isNull()) { lastChan = chan; } + + lmg->loadValues(lastChan, grp0); } } } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 21a4ef9bc67..fe50d5ea582 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -29,6 +29,7 @@ #include "Controls.h" #include "LedCheckbox.h" #include "LinkedModelGroups.h" +#include "stdshims.h" /* @@ -37,7 +38,7 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, - LinkedModelGroup *model, int colNum, int nProc, const QString& name) : + LinkedModelGroup *model, std::size_t colNum, std::size_t nProc, const QString& name) : QGroupBox(parent), m_colNum(colNum), m_isLinking(model->isLinking()), @@ -45,7 +46,7 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, { if (model->models().size()) { - int curProc = model->curProc(); + std::size_t curProc = model->curProc(); QString chanName; if (name.isNull()) { @@ -53,10 +54,12 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, { case 1: break; // don't display any channel name case 2: - chanName = QObject::tr(curProc ? "Right" : "Left"); + chanName = curProc + ? QObject::tr("Right") + : QObject::tr("Left"); break; default: - chanName = QObject::tr("Channel ") + QString::number(curProc + 1); + chanName = QObject::tr("Channel %1").arg(curProc + 1); break; } } @@ -79,16 +82,17 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) { // reconnect models using ModelInfo = LinkedModelGroup::ModelInfo; - std::vector>::iterator itr = m_controls.begin(); std::vector models = group->models(); Q_ASSERT(m_controls.size() == models.size()); - for (const ModelInfo& mdl : models) { (*itr++)->setModel(mdl.m_model); } + for (std::size_t i = 0; i < models.size(); ++i) + { + m_controls[i]->setModel(models[i].m_model); + } - std::size_t count = 0; - for (std::unique_ptr& led : m_leds) + for (std::size_t i = 0; i < m_leds.size(); ++i) { - led->setModel(group->linkEnabledModel(count++)); + m_leds[i]->setModel(group->linkEnabledModel(i)); } } @@ -97,7 +101,7 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) void LinkedModelGroupView::addControl(Control* ctrl) { - int colNum2 = m_colNum * (1 + m_isLinking); + int colNum2 = static_cast(m_colNum * (1 + m_isLinking)); int wdgNum = static_cast(m_controls.size() * (1 + m_isLinking)); if (ctrl) { @@ -106,8 +110,8 @@ void LinkedModelGroupView::addControl(Control* ctrl) // start in row one, add widgets cell by cell if (m_isLinking) { - LedCheckBox* cb = new LedCheckBox(qobject_cast( - ctrl->topWidget()->parent())); + LedCheckBox* cb = new LedCheckBox( + ctrl->topWidget()->parentWidget()); m_grid->addWidget(cb, y, x); m_leds.push_back(std::unique_ptr(cb)); } @@ -163,8 +167,8 @@ LinkedModelGroupsView::LinkedModelGroupsView( { if (ctrlBase->multiChannelLinkModel()) { - m_multiChannelLink.reset(new LedCheckBox(QObject::tr("Link Channels"), - nullptr)); + m_multiChannelLink = make_unique + (QObject::tr("Link Channels"), nullptr); } } @@ -178,15 +182,21 @@ void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) m_multiChannelLink->setModel(groups->multiChannelLinkModel()); } - for (std::size_t i = 0; groups->getGroup(i) && getGroupView(i); ++i) + LinkedModelGroupView* groupView; + LinkedModelGroup* group; + for (std::size_t i = 0; + (group = groups->getGroup(i)) && (groupView = getGroupView(i)); + ++i) { - getGroupView(i)->modelChanged(groups->getGroup(i)); + groupView->modelChanged(group); } } +// If you wonder why the default deleter can not be used: +// https://stackoverflow.com/questions/9954518 void LinkedModelGroupsView::MultiChannelLinkDeleter:: operator()(LedCheckBox *l) { delete l; } From 2ad9db692ecb876fb8601294122507a15ff54ae6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 19 Jun 2019 01:38:15 +0200 Subject: [PATCH 039/120] Update connect instructions --- include/LinkedModelGroups.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index d78ce0ec6f6..de2f4543357 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -138,17 +138,17 @@ class LinkedModelGroup : public Model A typical application are two mono plugins making a stereo plugin. - Inheriting classes need to do the following connections, where the slots - must be defined by those classes and call the equal named functions of this - class: + Inheriting classes need to do the following connections: \code if (multiChannelLinkModel()) { - connect(multiChannelLinkModel(), SIGNAL(dataChanged()), - this, SLOT(updateLinkStatesFromGlobal())); - connect(getGroup(0), SIGNAL(linkStateChanged(std::size_t, bool)), - this, SLOT(linkPort(std::size_t, bool))); + connect(multiChannelLinkModel(), &BoolModel::dataChanged, + this, [this](){updateLinkStatesFromGlobal();}, + Qt::DirectConnection); + connect(getGroup(0), &LinkedModelGroup::linkStateChanged, + this, [this](std::size_t id, bool value){ + linkModel(id, value);}, Qt::DirectConnection); } \endcode From c6ba10b2a3f2c32bc47f45f188c7035a351fe32d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 29 Jun 2019 09:54:14 +0200 Subject: [PATCH 040/120] Lv2Effect/Instrument: Fix translate section --- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 40046da60bd..2a5f10bb38c 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -40,7 +40,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("Lv2Effect", + QT_TRANSLATE_NOOP("pluginBrowser", "plugin for using arbitrary LV2-effects inside LMMS."), "Johannes Lorenz ", 0x0100, diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index b601419fd50..701e62f9a86 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -50,7 +50,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = { STRINGIFY(PLUGIN_NAME), "LV2", - QT_TRANSLATE_NOOP("Lv2Instrument", + QT_TRANSLATE_NOOP("pluginBrowser", "plugin for using arbitrary LV2 instruments inside LMMS."), "Johannes Lorenz ", 0x0100, From 12291623ec5b6ed1ddf353b2962b26f1cc8afcfa Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 29 Jun 2019 10:34:55 +0200 Subject: [PATCH 041/120] Replace redirecting slots by lambdas --- include/Lv2ViewBase.h | 2 +- plugins/Lv2Effect/Lv2FxControlDialog.cpp | 32 +++++------------------- plugins/Lv2Effect/Lv2FxControlDialog.h | 5 ---- plugins/Lv2Instrument/Lv2Instrument.cpp | 29 +++++---------------- plugins/Lv2Instrument/Lv2Instrument.h | 1 - 5 files changed, 13 insertions(+), 56 deletions(-) diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index 57eb9a3932a..31b33ac04eb 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -68,7 +68,7 @@ class Lv2ViewBase : public LinkedModelGroupsView class QPushButton *m_toggleUIButton = nullptr; class QPushButton *m_helpButton = nullptr; - // to be called by child slots + void toggleUI(); void toggleHelp(bool visible); // to be called by child virtuals diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index ccebd5fca08..df3b217a019 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -47,16 +47,16 @@ Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : Lv2ViewBase(this, controls) { if (m_reloadPluginButton) { - connect(m_reloadPluginButton, SIGNAL(toggled(bool)), - this, SLOT(reloadPlugin())); + connect(m_reloadPluginButton, &QPushButton::toggled, + this, [this](){ lv2Controls()->reloadPlugin(); }); } if (m_toggleUIButton) { - connect(m_toggleUIButton, SIGNAL(toggled(bool)), - this, SLOT(toggleUI())); + connect(m_toggleUIButton, &QPushButton::toggled, + this, [this](){ toggleUI(); }); } if (m_helpButton) { - connect(m_helpButton, SIGNAL(toggled(bool)), - this, SLOT(toggleHelp(bool))); + connect(m_helpButton, &QPushButton::toggled, + this, [this](bool visible){ toggleHelp(visible); }); } // for Effects, modelChanged only goes to the top EffectView // we need to call it manually @@ -66,26 +66,6 @@ Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : -void Lv2FxControlDialog::reloadPlugin() { lv2Controls()->reloadPlugin(); } - - - - -void Lv2FxControlDialog::toggleUI() -{ -} - - - - -void Lv2FxControlDialog::toggleHelp(bool visible) -{ - Lv2ViewBase::toggleHelp(visible); -} - - - - Lv2FxControls *Lv2FxControlDialog::lv2Controls() { return static_cast(m_effectControls); diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.h b/plugins/Lv2Effect/Lv2FxControlDialog.h index d7bace0db19..9edd7441454 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.h +++ b/plugins/Lv2Effect/Lv2FxControlDialog.h @@ -39,11 +39,6 @@ class Lv2FxControlDialog : public EffectControlDialog, public Lv2ViewBase Lv2FxControlDialog(Lv2FxControls *controls); virtual ~Lv2FxControlDialog() override {} -private slots: - void reloadPlugin(); - void toggleUI(); - void toggleHelp(bool visible); - private: Lv2FxControls *lv2Controls(); void modelChanged() override; diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 701e62f9a86..23835267457 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -235,16 +235,16 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : { setAutoFillBackground(true); if (m_reloadPluginButton) { - connect(m_reloadPluginButton, SIGNAL(toggled(bool)), - this, SLOT(reloadPlugin())); + connect(m_reloadPluginButton, &QPushButton::toggled, + this, [this](){ castModel()->reloadPlugin();} ); } if (m_toggleUIButton) { - connect(m_toggleUIButton, SIGNAL(toggled(bool)), - this, SLOT(toggleUI())); + connect(m_toggleUIButton, &QPushButton::toggled, + this, [this](){ toggleUI(); }); } if (m_helpButton) { - connect(m_helpButton, SIGNAL(toggled(bool)), - this, SLOT(toggleHelp(bool))); + connect(m_helpButton, &QPushButton::toggled, + this, [this](bool visible){ toggleHelp(visible); }); } } @@ -291,15 +291,6 @@ void Lv2InsView::dropEvent(QDropEvent *_de) -void Lv2InsView::reloadPlugin() -{ - Lv2Instrument *model = castModel(); - model->reloadPlugin(); -} - - - - void Lv2InsView::toggleUI() { } @@ -307,14 +298,6 @@ void Lv2InsView::toggleUI() -void Lv2InsView::toggleHelp(bool visible) -{ - Lv2ViewBase::toggleHelp(visible); -} - - - - void Lv2InsView::modelChanged() { Lv2ViewBase::modelChanged(castModel()); diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 6f7f8989688..227dacb2472 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -117,7 +117,6 @@ class Lv2InsView : public InstrumentView, public Lv2ViewBase private slots: void reloadPlugin(); void toggleUI(); - void toggleHelp(bool visible); private: void modelChanged(); From 88bb00cab5a2eef59879c25d771309fe76d2ccde Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 29 Jun 2019 14:24:43 +0200 Subject: [PATCH 042/120] Cleanup includes --- include/Lv2ControlBase.h | 8 +++----- plugins/Lv2Effect/Lv2Effect.cpp | 3 +-- plugins/Lv2Effect/Lv2Effect.h | 2 -- plugins/Lv2Effect/Lv2FxControlDialog.cpp | 10 ---------- plugins/Lv2Instrument/Lv2Instrument.cpp | 16 +++++----------- plugins/Lv2Instrument/Lv2Instrument.h | 2 -- src/core/lv2/Lv2Basics.cpp | 4 +--- src/core/lv2/Lv2ControlBase.cpp | 1 - src/core/lv2/Lv2SubPluginFeatures.cpp | 10 ---------- 9 files changed, 10 insertions(+), 46 deletions(-) diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 0380190db41..95c154f330b 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -29,12 +29,10 @@ #ifdef LMMS_HAVE_LV2 -// general LMMS includes +#include + #include "DataFile.h" -#include "lmms_basics.h" #include "LinkedModelGroups.h" -#include "Lv2Basics.h" -#include "Model.h" #include "Plugin.h" class Lv2Proc; @@ -86,7 +84,7 @@ class Lv2ControlBase : public LinkedModelGroups //! @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(Model *that, const QString& uri); + Lv2ControlBase(class Model *that, const QString& uri); virtual ~Lv2ControlBase() override; //! Must be checked after ctor or reload bool isValid() const { return m_valid; } diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 2a5f10bb38c..abf59a0eb6b 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -27,9 +27,8 @@ #include #include -#include "AutomatableModel.h" -#include "Lv2FxControlDialog.h" #include "Lv2SubPluginFeatures.h" + #include "embed.h" #include "plugin_export.h" diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index 787fd5eff3d..adf9157ae95 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -25,8 +25,6 @@ #ifndef LV2_EFFECT_H #define LV2_EFFECT_H -#include - #include "Effect.h" #include "Lv2FxControls.h" diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index df3b217a019..9f00031174f 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -25,21 +25,11 @@ #include "Lv2FxControlDialog.h" #include -#include -#include -#include -#include #include -#include #include -#include "Knob.h" -#include "LcdSpinBox.h" -#include "LedCheckbox.h" #include "Lv2Effect.h" #include "Lv2FxControls.h" -#include "embed.h" -#include "gui_templates.h" Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 23835267457..05416b30aae 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -25,21 +25,15 @@ #include "Lv2Instrument.h" #include -#include -#include -#include -#include +#include -#include "AutomatableModel.h" -#include "ControllerConnection.h" +#include "Engine.h" #include "InstrumentPlayHandle.h" #include "InstrumentTrack.h" -#include "Mixer.h" -#include "LedCheckbox.h" -#include "Lv2Proc.h" #include "Lv2SubPluginFeatures.h" -#include "StringPairDrag.h" // DnD -#include "gui_templates.h" +#include "Mixer.h" +#include "StringPairDrag.h" + #include "embed.h" #include "plugin_export.h" diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 227dacb2472..1c3b096d3a7 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -25,9 +25,7 @@ #ifndef LV2_INSTRUMENT_H #define LV2_INSTRUMENT_H -#include #include -#include #include "Instrument.h" #include "InstrumentView.h" diff --git a/src/core/lv2/Lv2Basics.cpp b/src/core/lv2/Lv2Basics.cpp index 6cce3367877..18b1a8e8e9c 100644 --- a/src/core/lv2/Lv2Basics.cpp +++ b/src/core/lv2/Lv2Basics.cpp @@ -22,12 +22,10 @@ * */ -#include "Lv2ControlBase.h" +#include "Lv2Basics.h" #ifdef LMMS_HAVE_LV2 -#include "Lv2Basics.h" - QString qStringFromPluginNode(const LilvPlugin* plug, LilvNode* (*getFunc)(const LilvPlugin*)) { diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 5d2dc41e061..5b62f5add22 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -28,7 +28,6 @@ #include -#include "AutomatableModel.h" #include "Engine.h" #include "Lv2Manager.h" #include "Lv2Proc.h" diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 8506b1bfa35..91382d80fe4 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -29,23 +29,13 @@ #ifdef LMMS_HAVE_LV2 -#include #include -#include #include #include -#include -#include -#include -#include "AudioDevice.h" -#include "ConfigManager.h" #include "Engine.h" -#include "Mixer.h" -#include "PluginFactory.h" #include "Lv2Basics.h" #include "Lv2Manager.h" -#include "embed.h" const LilvPlugin *Lv2SubPluginFeatures::getPlugin( From 538b5a58c6bd39260c2947455e1ccc5777ff89ab Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 26 Jul 2019 10:23:55 +0200 Subject: [PATCH 043/120] Fix assertion for #LinkedModelGroups == 1 --- src/core/LinkedModelGroups.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 2029c15d374..f00f41e88a9 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -83,7 +83,8 @@ void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, std::size_t id) void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, const LinkedModelGroup *lmg0) { - Q_ASSERT(lmg0->isLinking()); + // if multiple lmgs, the first one must currently be the linking one + Q_ASSERT(this == lmg0 || lmg0->isLinking()); for (std::size_t idx = 0; idx < m_models.size(); ++idx) { if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) From dcd7482c1394ccf292f01317b7c5fde930ba6182 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Jul 2019 02:45:07 +0200 Subject: [PATCH 044/120] LinkedModelGroups: Priv members -> nested struct Reason will be clear in the following commit. --- include/LinkedModelGroups.h | 21 ++++++++++++--------- src/core/LinkedModelGroups.cpp | 28 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index de2f4543357..35e2e46c626 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -76,7 +76,7 @@ class LinkedModelGroup : public Model //! @see linkControls void unlinkControls(LinkedModelGroup *other, std::size_t id); //! Return whether this is the first of more than one processors - bool isLinking() const { return m_linkEnabled.size(); } + bool isLinking() const { return d.m_linkEnabled.size(); } /* Models @@ -91,14 +91,14 @@ class LinkedModelGroup : public Model class BoolModel* linkEnabledModel(std::size_t id) { - return m_linkEnabled[id]; + return d.m_linkEnabled[id]; } const class BoolModel* linkEnabledModel(std::size_t id) const { - return m_linkEnabled[id]; + return d.m_linkEnabled[id]; } - std::vector& models() { return m_models; } - const std::vector& models() const { return m_models; } + std::vector& models() { return d.m_models; } + const std::vector& models() const { return d.m_models; } /* Load/Save @@ -121,10 +121,13 @@ class LinkedModelGroup : public Model void addModel(class AutomatableModel* model, const QString& name); private: - //! models for the per-control link-enabled models - std::vector m_linkEnabled; - //! models for the controls; the vector defines indices for the controls - std::vector m_models; + struct + { + //! models for the per-control link-enabled models + std::vector m_linkEnabled; + //! models for the controls; the vector defines indices for the controls + std::vector m_models; + } d; std::size_t m_curProc; }; diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index f00f41e88a9..0fab41da637 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -42,10 +42,10 @@ void LinkedModelGroup::makeLinkingProc() { - for (std::size_t i = 0; i < m_models.size(); ++i) + for (std::size_t i = 0; i < models().size(); ++i) { BoolModel* bmo = new BoolModel(true, this, tr("Link channels")); - m_linkEnabled.push_back(bmo); + d.m_linkEnabled.push_back(bmo); connect(bmo, &BoolModel::dataChanged, this, [this, bmo, i]() { emit linkStateChanged(i, bmo->value()); }); } @@ -56,7 +56,7 @@ void LinkedModelGroup::makeLinkingProc() void LinkedModelGroup::linkAllModels(bool state) { - for (BoolModel* bmo : m_linkEnabled) { bmo->setValue(state); } + for (BoolModel* bmo : d.m_linkEnabled) { bmo->setValue(state); } } @@ -65,7 +65,7 @@ void LinkedModelGroup::linkAllModels(bool state) void LinkedModelGroup::linkControls(LinkedModelGroup *other, std::size_t id) { AutomatableModel::linkModels( - m_models[id].m_model, other->m_models[id].m_model); + models()[id].m_model, other->models()[id].m_model); } @@ -74,7 +74,7 @@ void LinkedModelGroup::linkControls(LinkedModelGroup *other, std::size_t id) void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, std::size_t id) { AutomatableModel::unlinkModels( - m_models[id].m_model, other->m_models[id].m_model); + models()[id].m_model, other->models()[id].m_model); } @@ -85,12 +85,12 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, { // if multiple lmgs, the first one must currently be the linking one Q_ASSERT(this == lmg0 || lmg0->isLinking()); - for (std::size_t idx = 0; idx < m_models.size(); ++idx) + for (std::size_t idx = 0; idx < models().size(); ++idx) { if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) { // try to load, if it fails, this will load a sane initial value - m_models[idx].m_model->saveSettings(doc, that, m_models[idx].m_name); + models()[idx].m_model->saveSettings(doc, that, models()[idx].m_name); } else { @@ -104,9 +104,9 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) { - for (std::size_t i = 0; i < m_linkEnabled.size(); ++i) + for (std::size_t i = 0; i < d.m_linkEnabled.size(); ++i) { - m_linkEnabled[i]->saveSettings(doc, that, m_models[i].m_name); + d.m_linkEnabled[i]->saveSettings(doc, that, models()[i].m_name); } } @@ -116,12 +116,12 @@ void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) void LinkedModelGroup::loadValues(const QDomElement &that, const LinkedModelGroup* lmg0) { - for (std::size_t idx = 0; idx < m_models.size(); ++idx) + for (std::size_t idx = 0; idx < models().size(); ++idx) { if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) { // try to load, if it fails, this will load a sane initial value - m_models[idx].m_model->loadSettings(that, m_models[idx].m_name); + models()[idx].m_model->loadSettings(that, models()[idx].m_name); } else { @@ -135,9 +135,9 @@ void LinkedModelGroup::loadValues(const QDomElement &that, void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) { - for (std::size_t i = 0; i < m_linkEnabled.size(); ++i) + for (std::size_t i = 0; i < d.m_linkEnabled.size(); ++i) { - m_linkEnabled[i]->loadSettings(that, m_models[i].m_name); + d.m_linkEnabled[i]->loadSettings(that, models()[i].m_name); } } @@ -146,7 +146,7 @@ void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) { - m_models.emplace_back(name, model); + models().emplace_back(name, model); } From 2ce0b7fb14198fa1b2294fe92b04c361618a3bbf Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Jul 2019 02:47:26 +0200 Subject: [PATCH 045/120] Implement clearing models This uses RAII of the nested struct to destroy private members. --- include/LinkedModelGroups.h | 2 ++ src/core/LinkedModelGroups.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 35e2e46c626..9725e5956b3 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -119,6 +119,8 @@ class LinkedModelGroup : public Model protected: //! Register a further model void addModel(class AutomatableModel* model, const QString& name); + //! Remove all models and all link-enabled models + void clearModels(); private: struct diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 0fab41da637..6048f1265f9 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -152,6 +152,16 @@ void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) +void LinkedModelGroup::clearModels() +{ + using datatype = decltype(d); + d.~datatype(); + new (&d) datatype(); +} + + + + /* LinkedModelGroups */ From b3d1fb1ba48d91a01592acd2e9b09c2e09cc3c71 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 23 Jul 2019 15:29:55 +0200 Subject: [PATCH 046/120] Hide model vector in derived classes --- include/LinkedModelGroups.h | 14 ++++++++++++-- src/core/LinkedModelGroups.cpp | 13 ++++++++++++- src/gui/widgets/LinkedModelGroupViews.cpp | 10 ++++------ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 9725e5956b3..547a05b6288 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -97,8 +97,14 @@ class LinkedModelGroup : public Model { return d.m_linkEnabled[id]; } - std::vector& models() { return d.m_models; } - const std::vector& models() const { return d.m_models; } + + AutomatableModel* model(std::size_t i) { return d.m_models[i].m_model; } + const AutomatableModel* model(std::size_t i) const + { + return d.m_models[i].m_model; + } + AutomatableModel* modelWithName(const QString& name) const; + std::size_t modelNum() const { return models().size(); } /* Load/Save @@ -123,6 +129,10 @@ class LinkedModelGroup : public Model void clearModels(); private: + // TODO: remove + std::vector& models() { return d.m_models; } + const std::vector& models() const { return d.m_models; } + struct { //! models for the per-control link-enabled models diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 6048f1265f9..31d593c2429 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -74,7 +74,18 @@ void LinkedModelGroup::linkControls(LinkedModelGroup *other, std::size_t id) void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, std::size_t id) { AutomatableModel::unlinkModels( - models()[id].m_model, other->models()[id].m_model); + models()[id].m_model, other->models()[id].m_model); +} + + + + +AutomatableModel *LinkedModelGroup::modelWithName(const QString &name) const +{ + auto itr = std::find_if(models().begin(), models().end(), + [&name](const ModelInfo& mi) -> bool + { return mi.m_name == name; }); + return itr == models().end() ? nullptr : itr->m_model; } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index fe50d5ea582..188f18a1523 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -44,7 +44,7 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, m_isLinking(model->isLinking()), m_grid(new QGridLayout(this)) { - if (model->models().size()) + if (model->modelNum()) { std::size_t curProc = model->curProc(); QString chanName; @@ -81,13 +81,11 @@ LinkedModelGroupView::~LinkedModelGroupView() {} void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) { // reconnect models - using ModelInfo = LinkedModelGroup::ModelInfo; - std::vector models = group->models(); - Q_ASSERT(m_controls.size() == models.size()); + Q_ASSERT(m_controls.size() == group->modelNum()); - for (std::size_t i = 0; i < models.size(); ++i) + for (std::size_t i = 0; i < group->modelNum(); ++i) { - m_controls[i]->setModel(models[i].m_model); + m_controls[i]->setModel(group->model(i)); } for (std::size_t i = 0; i < m_leds.size(); ++i) From 0960b061d9d02a47419418d8be20172a9fc42e92 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 24 Jul 2019 04:12:27 +0200 Subject: [PATCH 047/120] Fix unique_ptr compiler issues --- include/LinkedModelGroups.h | 5 ++++- src/core/LinkedModelGroups.cpp | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 547a05b6288..f6c85631b55 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -208,9 +208,12 @@ class LinkedModelGroups virtual const LinkedModelGroup* getGroup(std::size_t idx) const = 0; private: + // Implement deletion in the CPP file: + struct BoolModelDeleter { void operator()(class BoolModel* l); }; + //! Model for the "global" linking //! Only allocated if #processors > 1 - std::unique_ptr m_multiChannelLinkModel; + std::unique_ptr m_multiChannelLinkModel; //! Force updateLinkStatesFromGlobal() to not unlink any ports //! Implementation detail, see linkPort() implementation diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 31d593c2429..58e654bdf76 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -187,7 +187,8 @@ LinkedModelGroups::~LinkedModelGroups() {} void LinkedModelGroups::createMultiChannelLinkModel() { - m_multiChannelLinkModel = make_unique(true, nullptr); + m_multiChannelLinkModel = + make_unique(true, nullptr); } @@ -322,3 +323,9 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) } + + +void LinkedModelGroups::BoolModelDeleter::operator()(BoolModel *l) +{ + delete l; +} From d9f61d0eb426c453e0d43de9b82758fac4ae6962 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 17 Aug 2019 18:21:14 +0200 Subject: [PATCH 048/120] Fix types (code review) * Always use std::size_t for model and proc nums * Always use fpp_t for frame numbers --- include/Lv2ControlBase.h | 2 +- include/Lv2Ports.h | 32 ++++++++++--------------- include/Lv2Proc.h | 8 +++---- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 2 +- src/core/lv2/Lv2ControlBase.cpp | 4 ++-- src/core/lv2/Lv2Ports.cpp | 5 ++-- src/core/lv2/Lv2Proc.cpp | 23 +++++++++--------- 8 files changed, 37 insertions(+), 41 deletions(-) diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 95c154f330b..458414231ee 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -105,7 +105,7 @@ class Lv2ControlBase : public LinkedModelGroups //! 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(unsigned frames); + void run(fpp_t frames); /* load/save, must be called from virtuals diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index df50619c8a1..5f5575c0490 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -78,30 +78,24 @@ struct Unknown; struct ConstVisitor { -#define CAN_VISIT(clss) \ -virtual void visit(const Lv2Ports::clss& ) {} - - CAN_VISIT(ControlPortBase) - CAN_VISIT(Control) - CAN_VISIT(Audio) - CAN_VISIT(Cv) - CAN_VISIT(Unknown) + virtual void visit(const Lv2Ports::ControlPortBase& ) {} + virtual void visit(const Lv2Ports::Control& ) {} + virtual void visit(const Lv2Ports::Audio& ) {} + virtual void visit(const Lv2Ports::Cv& ) {} + virtual void visit(const Lv2Ports::Unknown& ) {} + virtual ~ConstVisitor(); -#undef CAN_VISIT }; struct Visitor { -#define CAN_VISIT(clss) \ -virtual void visit(Lv2Ports::clss& ) {} - - CAN_VISIT(ControlPortBase) - CAN_VISIT(Control) - CAN_VISIT(Audio) - CAN_VISIT(Cv) - CAN_VISIT(Unknown) + virtual void visit(Lv2Ports::ControlPortBase& ) {} + virtual void visit(Lv2Ports::Control& ) {} + virtual void visit(Lv2Ports::Audio& ) {} + virtual void visit(Lv2Ports::Cv& ) {} + virtual void visit(Lv2Ports::Unknown& ) {} + virtual ~Visitor(); -#undef CAN_VISIT }; struct Meta @@ -114,7 +108,7 @@ struct Meta bool m_optional = false; bool m_used = true; - std::vector get(const LilvPlugin* plugin, unsigned portNum); + std::vector get(const LilvPlugin* plugin, std::size_t portNum); }; struct PortBase : public Meta diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index a673dfa8e1c..0823df5741f 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -62,7 +62,7 @@ class Lv2Proc : public LinkedModelGroup /* ctor/dtor */ - Lv2Proc(const LilvPlugin* plugin, Model *parent, int curProc); + Lv2Proc(const LilvPlugin* plugin, Model *parent, std::size_t curProc); virtual ~Lv2Proc(); //! Must be checked after ctor or reload bool isValid() const { return m_valid; } @@ -104,7 +104,7 @@ class Lv2Proc : public LinkedModelGroup void copyBuffersToCore(sampleFrame *buf, unsigned offset, unsigned num, fpp_t frames) const; //! Run the Lv2 plugin instance for @param frames frames - void run(unsigned frames); + void run(fpp_t frames); /* misc @@ -138,9 +138,9 @@ class Lv2Proc : public LinkedModelGroup //! allocate m_ports, fill all with metadata, and assign meaning of ports void createPorts(); //! fill m_ports[portNum] with metadata - void createPort(unsigned portNum); + void createPort(std::size_t portNum); //! connect m_ports[portNum] with Lv2 - void connectPort(unsigned num); + void connectPort(std::size_t num); void dumpPort(std::size_t num); diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index abf59a0eb6b..e19b37a1ed3 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -78,7 +78,7 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) m_controls.copyModelsFromLmms(); // m_pluginMutex.lock(); - ctrl.run(static_cast(frames)); + ctrl.run(frames); // m_pluginMutex.unlock(); ctrl.copyBuffersToLmms(buf, frames); diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 05416b30aae..c5eca0d3264 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -167,7 +167,7 @@ void Lv2Instrument::play(sampleFrame *buf) fpp_t fpp = Engine::mixer()->framesPerPeriod(); - run(static_cast(fpp)); + run(fpp); copyBuffersToLmms(buf, fpp); diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 5b62f5add22..51b233aace0 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -50,7 +50,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : { if (m_plugin) { - int procId = 0; + std::size_t procId = 0; int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo while (channelsLeft > 0) { @@ -147,7 +147,7 @@ void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { -void Lv2ControlBase::run(unsigned frames) { +void Lv2ControlBase::run(fpp_t frames) { for (std::unique_ptr& c : m_procs) { c->run(frames); } } diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 1b78846a985..df5df690875 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -82,7 +82,7 @@ const char *toStr(Vis pv) std::vector Meta::get(const LilvPlugin *plugin, - unsigned int portNum) + std::size_t portNum) { std::vector portIssues; auto issue = [&portIssues](PluginIssueType i, std::string msg = "") { @@ -90,7 +90,8 @@ std::vector Meta::get(const LilvPlugin *plugin, Lv2Manager* man = Engine::getLv2Manager(); - const LilvPort* lilvPort = lilv_plugin_get_port_by_index(plugin, portNum); + const LilvPort* lilvPort = lilv_plugin_get_port_by_index( + plugin, static_cast(portNum)); auto portFunc = [&plugin, &lilvPort, &man]( bool (*fptr)(const LilvPlugin*, const LilvPort*, const LilvNode*), diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 75f9f108409..412147f6279 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -95,7 +95,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, -Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent, int curProc) : +Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent, std::size_t curProc) : LinkedModelGroup(parent, curProc), m_plugin(plugin) { @@ -218,9 +218,9 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf, -void Lv2Proc::run(unsigned frames) +void Lv2Proc::run(fpp_t frames) { - lilv_instance_run(m_instance, frames); + lilv_instance_run(m_instance, static_cast(frames)); } @@ -249,7 +249,7 @@ void Lv2Proc::initPlugin() if (m_instance) { - for (unsigned portNum = 0; portNum < m_ports.size(); ++portNum) + for (std::size_t portNum = 0; portNum < m_ports.size(); ++portNum) connectPort(portNum); lilv_instance_activate(m_instance); } @@ -285,13 +285,13 @@ void Lv2Proc::loadFileInternal(const QString &file) -void Lv2Proc::createPort(unsigned portNum) +void Lv2Proc::createPort(std::size_t portNum) { Lv2Ports::Meta meta; meta.get(m_plugin, portNum); const LilvPort* lilvPort = lilv_plugin_get_port_by_index(m_plugin, - portNum); + static_cast(portNum)); Lv2Ports::PortBase* port; if (meta.m_type == Lv2Ports::Type::Control) { @@ -424,10 +424,10 @@ void Lv2Proc::createPorts() } }; - unsigned maxPorts = lilv_plugin_get_num_ports(m_plugin); + std::size_t maxPorts = lilv_plugin_get_num_ports(m_plugin); m_ports.resize(maxPorts); - for (unsigned portNum = 0; portNum < maxPorts; ++portNum) + for (std::size_t portNum = 0; portNum < maxPorts; ++portNum) { createPort(portNum); RegisterPort registerPort; @@ -447,10 +447,11 @@ void Lv2Proc::createPorts() struct ConnectPorts : public Lv2Ports::Visitor { - unsigned m_num; + std::size_t m_num; LilvInstance* m_instance; void con(void* location) { - lilv_instance_connect_port(m_instance, m_num, location); + lilv_instance_connect_port(m_instance, + static_cast(m_num), location); } void visit(Lv2Ports::Control& ctrl) override { con(&ctrl.m_val); } void visit(Lv2Ports::Audio& audio) override { @@ -461,7 +462,7 @@ struct ConnectPorts : public Lv2Ports::Visitor ConnectPorts::~ConnectPorts() {} -void Lv2Proc::connectPort(unsigned num) +void Lv2Proc::connectPort(std::size_t num) { ConnectPorts connect; connect.m_num = num; From 96a73be04ece805419c65fbfad55786dd3d7854d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 17 Aug 2019 19:35:35 +0200 Subject: [PATCH 049/120] Code review: Minor corrections --- include/Lv2Ports.h | 4 ++-- include/Lv2SubPluginFeatures.h | 6 +++--- src/core/lv2/Lv2Basics.cpp | 4 ++-- src/core/lv2/Lv2Proc.cpp | 22 +++++++++++----------- src/core/lv2/Lv2SubPluginFeatures.cpp | 24 ++++++++++++------------ 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 5f5575c0490..06a99a8c67b 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -36,7 +36,7 @@ #include "lmms_basics.h" #include "PluginIssue.h" -struct ConnectPorts; +struct ConnectPortVisitor; namespace Lv2Ports { @@ -187,7 +187,7 @@ struct Audio : public PortBase bool m_sidechain; // the only case when data of m_buffer may be referenced: - friend struct ::ConnectPorts; + friend struct ::ConnectPortVisitor; }; struct Unknown : public PortBase diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h index d36c9023f96..edf42c45499 100644 --- a/include/Lv2SubPluginFeatures.h +++ b/include/Lv2SubPluginFeatures.h @@ -42,10 +42,10 @@ class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures static QString pluginName(const LilvPlugin *plug); public: - Lv2SubPluginFeatures(Plugin::PluginTypes _type); + Lv2SubPluginFeatures(Plugin::PluginTypes type); virtual void fillDescriptionWidget( - QWidget *_parent, const Key *k) const override; + QWidget *parent, const Key *k) const override; QString additionalFileExtensions(const Key &k) const override; QString displayName(const Key &k) const override; @@ -53,7 +53,7 @@ class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures const PixmapLoader *logo(const Key &k) const override; void listSubPluginKeys( - const Plugin::Descriptor *_desc, KeyList &_kl) const override; + const Plugin::Descriptor *desc, KeyList &kl) const override; }; #endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Basics.cpp b/src/core/lv2/Lv2Basics.cpp index 18b1a8e8e9c..6b03ffa2371 100644 --- a/src/core/lv2/Lv2Basics.cpp +++ b/src/core/lv2/Lv2Basics.cpp @@ -29,8 +29,8 @@ QString qStringFromPluginNode(const LilvPlugin* plug, LilvNode* (*getFunc)(const LilvPlugin*)) { - auto conv = lilv_node_as_string; - return QString::fromUtf8(conv(AutoLilvNode((*getFunc)(plug)).get())); + return QString::fromUtf8( + lilv_node_as_string(AutoLilvNode((*getFunc)(plug)).get())); } QString qStringFromPortName(const LilvPlugin* plug, const LilvPort* port) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 412147f6279..1af6c5aa66d 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -133,7 +133,7 @@ void Lv2Proc::dumpPorts() void Lv2Proc::copyModelsFromCore() { - struct FloatFromModel : public ConstModelVisitor + struct FloatFromModelVisitor : public ConstModelVisitor { const std::vector* m_scalePointMap; // in float m_res; // out @@ -152,7 +152,7 @@ void Lv2Proc::copyModelsFromCore() { if (ctrl.m_flow == Lv2Ports::Flow::Input) { - FloatFromModel ffm; + FloatFromModelVisitor ffm; ffm.m_scalePointMap = &ctrl.m_scalePointMap; ctrl.m_connectedModel->accept(ffm); ctrl.m_val = ffm.m_res; @@ -162,7 +162,7 @@ void Lv2Proc::copyModelsFromCore() { if (cv.m_flow == Lv2Ports::Flow::Input) { - FloatFromModel ffm; + FloatFromModelVisitor ffm; ffm.m_scalePointMap = &cv.m_scalePointMap; cv.m_connectedModel->accept(ffm); // dirty fix, needs better interpolation @@ -445,26 +445,26 @@ void Lv2Proc::createPorts() -struct ConnectPorts : public Lv2Ports::Visitor +struct ConnectPortVisitor : public Lv2Ports::Visitor { std::size_t m_num; LilvInstance* m_instance; - void con(void* location) { + void connectPort(void* location) { lilv_instance_connect_port(m_instance, static_cast(m_num), location); } - void visit(Lv2Ports::Control& ctrl) override { con(&ctrl.m_val); } + void visit(Lv2Ports::Control& ctrl) override { connectPort(&ctrl.m_val); } void visit(Lv2Ports::Audio& audio) override { - con(audio.isSideChain() ? nullptr : audio.m_buffer.data()); } - void visit(Lv2Ports::Unknown&) override { con(nullptr); } - ~ConnectPorts() override; + connectPort(audio.isSideChain() ? nullptr : audio.m_buffer.data()); } + void visit(Lv2Ports::Unknown&) override { connectPort(nullptr); } + ~ConnectPortVisitor() override; }; -ConnectPorts::~ConnectPorts() {} +ConnectPortVisitor::~ConnectPortVisitor() {} void Lv2Proc::connectPort(std::size_t num) { - ConnectPorts connect; + ConnectPortVisitor connect; connect.m_num = num; connect.m_instance = m_instance; m_ports[num]->accept(connect); diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 91382d80fe4..1014ae91688 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -58,27 +58,27 @@ QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug) -Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes _type) : - SubPluginFeatures(_type) +Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes type) : + SubPluginFeatures(type) { } -void Lv2SubPluginFeatures::fillDescriptionWidget( - QWidget *_parent, const Key *k) const +void Lv2SubPluginFeatures::fillDescriptionWidget(QWidget *parent, + const Key *k) const { const LilvPlugin *plug = getPlugin(*k); - QLabel *label = new QLabel(_parent); + QLabel *label = new QLabel(parent); label->setText(QWidget::tr("Name: ") + pluginName(plug)); - QLabel *label2 = new QLabel(_parent); + QLabel *label2 = new QLabel(parent); label2->setText(QWidget::tr("URI: ") + lilv_node_as_uri(lilv_plugin_get_uri(plug))); - QWidget *maker = new QWidget(_parent); + QWidget *maker = new QWidget(parent); QHBoxLayout *l = new QHBoxLayout(maker); l->setMargin(0); l->setSpacing(0); @@ -95,11 +95,11 @@ void Lv2SubPluginFeatures::fillDescriptionWidget( l->addWidget(maker_label); l->addWidget(maker_content, 1); - QWidget *copyright = new QWidget(_parent); + QWidget *copyright = new QWidget(parent); l = new QHBoxLayout(copyright); l->setMargin(0); l->setSpacing(0); - copyright->setMinimumWidth(_parent->minimumWidth()); + copyright->setMinimumWidth(parent->minimumWidth()); QLabel *copyright_label = new QLabel(copyright); copyright_label->setText(QWidget::tr("Copyright: ")); @@ -160,8 +160,8 @@ const PixmapLoader *Lv2SubPluginFeatures::logo( -void Lv2SubPluginFeatures::listSubPluginKeys( - const Plugin::Descriptor *_desc, KeyList &_kl) const +void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor *desc, + KeyList &kl) const { Lv2Manager *lv2Mgr = Engine::getLv2Manager(); for (const std::pair &pr : @@ -175,7 +175,7 @@ void Lv2SubPluginFeatures::listSubPluginKeys( atm["uri"] = QString::fromUtf8(pr.first.c_str()); const LilvPlugin* plug = pr.second.plugin(); - _kl.push_back(KeyType(_desc, pluginName(plug), atm)); + kl.push_back(KeyType(desc, pluginName(plug), atm)); //qDebug() << "Found LV2 sub plugin key of type" << // m_type << ":" << pr.first.c_str(); } From 9ab8910b67e507d43c218735ee10c8c82e67e53d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 17 Aug 2019 19:36:02 +0200 Subject: [PATCH 050/120] Code review: Improve loop readability --- src/core/lv2/Lv2Manager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index a49c73eade9..f84a0e27441 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -119,11 +119,11 @@ bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) lilv_plugin_class_get_uri(pc2)); }; bool isFound = false; - for (; - !(isFound = clssEq(clvss, search)) && !clssEq(clvss, root); + while (!(isFound = clssEq(clvss, search)) && !clssEq(clvss, root)) + { clvss = lilv_plugin_classes_get_by_uri(allClasses, - lilv_plugin_class_get_parent_uri(clvss)) - ) ; + lilv_plugin_class_get_parent_uri(clvss)); + } return isFound; } From 2ba3fece5b3cf2610a5be2ac50e2f59775ff3e30 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 4 Dec 2019 19:12:22 +0100 Subject: [PATCH 051/120] Controls: allow to return model view --- include/Controls.h | 5 +++++ src/gui/widgets/Controls.cpp | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/include/Controls.h b/include/Controls.h index e00649126c7..236abbc1199 100644 --- a/include/Controls.h +++ b/include/Controls.h @@ -53,6 +53,7 @@ class Control virtual void setModel(AutomatableModel* model) = 0; virtual AutomatableModel* model() = 0; + virtual class AutomatableModelView* modelView() = 0; virtual ~Control(); }; @@ -68,6 +69,7 @@ class KnobControl : public Control void setModel(AutomatableModel* model) override; FloatModel* model() override; + class AutomatableModelView* modelView() override; KnobControl(QWidget* parent = nullptr); ~KnobControl() override; @@ -86,6 +88,7 @@ class ComboControl : public Control void setModel(AutomatableModel* model) override; ComboBoxModel* model() override; + class AutomatableModelView* modelView() override; ComboControl(QWidget* parent = nullptr); ~ComboControl() override; @@ -102,6 +105,7 @@ class LcdControl : public Control void setModel(AutomatableModel* model) override; IntModel* model() override; + class AutomatableModelView* modelView() override; LcdControl(int numDigits, QWidget* parent = nullptr); ~LcdControl() override; @@ -120,6 +124,7 @@ class CheckControl : public Control void setModel(AutomatableModel* model) override; BoolModel *model() override; + class AutomatableModelView* modelView() override; CheckControl(QWidget* parent = nullptr); ~CheckControl() override; diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp index b5844706028..15b4e0d282a 100644 --- a/src/gui/widgets/Controls.cpp +++ b/src/gui/widgets/Controls.cpp @@ -52,6 +52,8 @@ void KnobControl::setModel(AutomatableModel *model) FloatModel *KnobControl::model() { return m_knob->model(); } +AutomatableModelView* KnobControl::modelView() { return m_knob; } + KnobControl::KnobControl(QWidget *parent) : m_knob(new Knob(parent)) {} @@ -69,6 +71,8 @@ void ComboControl::setModel(AutomatableModel *model) ComboBoxModel *ComboControl::model() { return m_combo->model(); } +AutomatableModelView* ComboControl::modelView() { return m_combo; } + ComboControl::ComboControl(QWidget *parent) : m_widget(new QWidget(parent)), m_combo(new ComboBox(nullptr)), @@ -97,6 +101,8 @@ void CheckControl::setModel(AutomatableModel *model) BoolModel *CheckControl::model() { return m_checkBox->model(); } +AutomatableModelView* CheckControl::modelView() { return m_checkBox; } + CheckControl::CheckControl(QWidget *parent) : m_widget(new QWidget(parent)), m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::Green)), @@ -123,6 +129,8 @@ void LcdControl::setModel(AutomatableModel *model) IntModel *LcdControl::model() { return m_lcd->model(); } +AutomatableModelView* LcdControl::modelView() { return m_lcd; } + LcdControl::LcdControl(int numDigits, QWidget *parent) : m_lcd(new LcdSpinBox(numDigits, parent)) { From 99cb22a0dd363c6a64c9263eb158089a11a4bd78 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 4 Dec 2019 19:12:27 +0100 Subject: [PATCH 052/120] Implement LinkedModelGroupLayout This implements a new layout that allows adding and removing models and contains a search bar. Adding and removing will be needed for spawning knobs on drag-drop (see #5095). This requires changes in LinkedModelGroup classes, though these classes will be removed soon (and placed into Lv2/Spa/... directly): Previously, the models for each processor have been stored in a vector, now they are in a map (LinkedModelGroups.h). In this step, the m_linkEnabled also have been pulled from a vector into a map (the same map now as the models). Note: If VST does not compile with this commit anymore, simply use `cmake -DPLUGIN_LIST=x;y;z` to not enable VST. This is an issue that has been fixed on master already. --- include/LinkedModelGroupLayout.h | 129 +++++++++ include/LinkedModelGroupViews.h | 19 +- include/LinkedModelGroups.h | 78 ++++-- src/core/LinkedModelGroups.cpp | 75 +++-- src/gui/CMakeLists.txt | 1 + src/gui/widgets/LinkedModelGroupLayout.cpp | 305 +++++++++++++++++++++ src/gui/widgets/LinkedModelGroupViews.cpp | 126 +++++++-- 7 files changed, 652 insertions(+), 81 deletions(-) create mode 100644 include/LinkedModelGroupLayout.h create mode 100644 src/gui/widgets/LinkedModelGroupLayout.cpp diff --git a/include/LinkedModelGroupLayout.h b/include/LinkedModelGroupLayout.h new file mode 100644 index 00000000000..3e0989c930a --- /dev/null +++ b/include/LinkedModelGroupLayout.h @@ -0,0 +1,129 @@ +/* + * LinkedModelGroupLayout.h - layout for LinkedModelGroup class + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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. + * + */ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LINKEDMODELGROUPLAYOUT_H +#define LINKEDMODELGROUPLAYOUT_H + +#include +#include +#include +class QLayoutItem; +class QRect; +class QString; + +/** + Layout for models + + Features a search bar, as well as looking up widgets with string keys + Keys have to be provided in the widgets' objectNames +*/ +class LinkedModelGroupLayout : public QLayout +{ + Q_OBJECT + +public: + explicit LinkedModelGroupLayout(QWidget *parent, + int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~LinkedModelGroupLayout() override; + + void addItem(QLayoutItem *item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem *itemAt(int index) const override; + QLayoutItem *itemByString(const QString& key) const; + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + QSize sizeHint() const override; + QLayoutItem *takeAt(int index) override; + +private slots: + void onTextChanged(const QString&); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + QMap::const_iterator pairAt(int index) const; + + QMultiMap m_itemMap; + int m_hSpace; + int m_vSpace; + // relevant dimension is width, as later, heightForWidth() will be called + // 400 looks good and is ~4 knobs in a row + constexpr const static int m_minWidth = 400; + class QLineEdit* m_searchBar; +}; + +#endif // LINKEDMODELGROUPLAYOUT_H diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 7e7c8f3e920..dad24ba5217 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -64,17 +64,28 @@ class LinkedModelGroupView : public QGroupBox protected: //! Add a control to this widget - void addControl(class Control *ctrl); + //! @warning This widget will own this control, do not free it + void addControl(class Control *ctrl, const std::string &id, + const std::string& display, bool removable); + + void removeControl(const QString &key); private: void makeAllGridCellsEqualSized(); + class LinkedModelGroup* m_model; + //! column number in surrounding grid in LinkedModelGroupsView std::size_t m_colNum; bool m_isLinking; - class QGridLayout* m_grid; - std::vector> m_controls; - std::vector> m_leds; + class LinkedModelGroupLayout* m_layout; + struct WidgetsPerModel + { + std::unique_ptr m_ctrl; + class LedCheckBox* m_led = nullptr; + }; + + std::map m_widgets; }; diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index f6c85631b55..f328d0a61db 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -49,7 +49,7 @@ class LinkedModelGroup : public Model Q_OBJECT signals: //! Signal emitted after any of the per-control link-enabled models switch - void linkStateChanged(std::size_t id, bool value); + void linkStateChanged(const std::string& str, bool value); public: /* @@ -71,12 +71,12 @@ class LinkedModelGroup : public Model //! also link or unlink them (via `LinkedModelGroups::linkModel()`) void linkAllModels(bool state); //! Link specified port with the associated port of @p other - //! @param id id of the port, conforming to m_models - void linkControls(LinkedModelGroup* other, std::size_t id); + //! @param id string identifier of the port + void linkControls(LinkedModelGroup* other, const std::string& id); //! @see linkControls - void unlinkControls(LinkedModelGroup *other, std::size_t id); + void unlinkControls(LinkedModelGroup *other, const std::string &id); //! Return whether this is the first of more than one processors - bool isLinking() const { return d.m_linkEnabled.size(); } + bool isLinking() const; /* Models @@ -85,27 +85,71 @@ class LinkedModelGroup : public Model { QString m_name; class AutomatableModel* m_model; + class BoolModel* m_linkEnabled = nullptr; + ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? ModelInfo(const QString& name, AutomatableModel* model) : m_name(name), m_model(model) {} }; - class BoolModel* linkEnabledModel(std::size_t id) + // TODO: refactor those 4 + AutomatableModel* model(const std::string& s) { - return d.m_linkEnabled[id]; + auto itr = d.m_models.find(s); + if(itr == d.m_models.end()) + throw std::runtime_error("..."); + return itr->second.m_model; } - const class BoolModel* linkEnabledModel(std::size_t id) const + + const AutomatableModel* model(const std::string& s) const + { + auto itr = d.m_models.find(s); + if(itr == d.m_models.end()) + throw std::runtime_error("..."); + return itr->second.m_model; + } + + class BoolModel* linkEnabledModel(const std::string& s) + { + auto itr = d.m_models.find(s); + if(itr == d.m_models.end()) + throw std::runtime_error("..."); + return itr->second.m_linkEnabled; + } + + const class BoolModel* linkEnabledModel(const std::string& s) const + { + auto itr = d.m_models.find(s); + if(itr == d.m_models.end()) + throw std::runtime_error("..."); + return itr->second.m_linkEnabled; + } + + template + void foreach_model(const Functor& ftor) { - return d.m_linkEnabled[id]; + for(auto itr = d.m_models.begin(); itr != d.m_models.end(); ++itr) + { + ftor(itr->first, itr->second); + } } - AutomatableModel* model(std::size_t i) { return d.m_models[i].m_model; } - const AutomatableModel* model(std::size_t i) const + template + void foreach_model(const Functor& ftor) const { - return d.m_models[i].m_model; + for(auto itr = d.m_models.cbegin(); itr != d.m_models.cend(); ++itr) + { + ftor(itr->first, itr->second); + } } + AutomatableModel* modelWithName(const QString& name) const; std::size_t modelNum() const { return models().size(); } + // this is bad style (redirecting into the sub-class), but this class + // will be married with the sub-classes (Lv2Proc, SpaProc) anyways, + // so let's do the dirty trick for now... + virtual void removeControl(AutomatableModel *) {} + /* Load/Save */ @@ -130,15 +174,13 @@ class LinkedModelGroup : public Model private: // TODO: remove - std::vector& models() { return d.m_models; } - const std::vector& models() const { return d.m_models; } + std::map& models() { return d.m_models; } + const std::map& models() const { return d.m_models; } struct { - //! models for the per-control link-enabled models - std::vector m_linkEnabled; //! models for the controls; the vector defines indices for the controls - std::vector m_models; + std::map m_models; } d; std::size_t m_curProc; @@ -188,7 +230,7 @@ class LinkedModelGroups //! of every other LinkedModelGroup //! @param model number conforming to getGroup() //! @param state True iff it should be linked - void linkModel(std::size_t model, bool state); + void linkModel(const std::string &model, bool state); //! Callback for the global linking LED void updateLinkStatesFromGlobal(); diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 58e654bdf76..1cc40dab0cf 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -42,13 +42,13 @@ void LinkedModelGroup::makeLinkingProc() { - for (std::size_t i = 0; i < models().size(); ++i) + foreach_model([this](const std::string& str, ModelInfo inf) { BoolModel* bmo = new BoolModel(true, this, tr("Link channels")); - d.m_linkEnabled.push_back(bmo); + inf.m_linkEnabled = bmo; connect(bmo, &BoolModel::dataChanged, this, - [this, bmo, i]() { emit linkStateChanged(i, bmo->value()); }); - } + [this, bmo, str]() { emit linkStateChanged(str, bmo->value()); }); + }); } @@ -56,25 +56,38 @@ void LinkedModelGroup::makeLinkingProc() void LinkedModelGroup::linkAllModels(bool state) { - for (BoolModel* bmo : d.m_linkEnabled) { bmo->setValue(state); } + // this will call AutomatableModel::linkModels (??? TODO) + for (auto pr : d.m_models) { pr.second.m_linkEnabled->setValue(state); } } -void LinkedModelGroup::linkControls(LinkedModelGroup *other, std::size_t id) +void LinkedModelGroup::linkControls(LinkedModelGroup *other, const std::string& id) { - AutomatableModel::linkModels( - models()[id].m_model, other->models()[id].m_model); + auto itr1 = models().find(id); + auto itr2 = other->models().find(id); + Q_ASSERT(itr1 != models().end() && itr2 != other->models().end()); + AutomatableModel::linkModels(itr1->second.m_model, itr2->second.m_model); } -void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, std::size_t id) +void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, const std::string& id) { - AutomatableModel::unlinkModels( - models()[id].m_model, other->models()[id].m_model); + auto itr1 = models().find(id); + auto itr2 = other->models().find(id); + Q_ASSERT(itr1 != models().end() && itr2 != other->models().end()); + AutomatableModel::unlinkModels(itr1->second.m_model, itr2->second.m_model); +} + + + + +bool LinkedModelGroup::isLinking() const { + return d.m_models.size() + ?!!d.m_models.begin()->second.m_linkEnabled : false; } @@ -83,9 +96,9 @@ void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, std::size_t id) AutomatableModel *LinkedModelGroup::modelWithName(const QString &name) const { auto itr = std::find_if(models().begin(), models().end(), - [&name](const ModelInfo& mi) -> bool - { return mi.m_name == name; }); - return itr == models().end() ? nullptr : itr->m_model; + [&name](const std::pair& pr) -> bool + { return pr.second.m_name == name; }); + return itr == models().end() ? nullptr : itr->second.m_model; } @@ -96,18 +109,18 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, { // if multiple lmgs, the first one must currently be the linking one Q_ASSERT(this == lmg0 || lmg0->isLinking()); - for (std::size_t idx = 0; idx < models().size(); ++idx) + foreach_model([&lmg0, this, &doc, &that](const std::string& str, ModelInfo& inf) { - if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) + if (this == lmg0 || !lmg0->linkEnabledModel(str)->value()) { // try to load, if it fails, this will load a sane initial value - models()[idx].m_model->saveSettings(doc, that, models()[idx].m_name); + inf.m_model->saveSettings(doc, that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ } else { // model has the same value as in the first LinkedModelGroup } - } + }); } @@ -115,10 +128,11 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) { - for (std::size_t i = 0; i < d.m_linkEnabled.size(); ++i) + //for (std::size_t i = 0; i < d.m_linkEnabled.size(); ++i) + foreach_model([&doc, &that](const std::string&, ModelInfo& inf) { - d.m_linkEnabled[i]->saveSettings(doc, that, models()[i].m_name); - } + inf.m_linkEnabled->saveSettings(doc, that, inf.m_name); + }); } @@ -127,18 +141,18 @@ void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) void LinkedModelGroup::loadValues(const QDomElement &that, const LinkedModelGroup* lmg0) { - for (std::size_t idx = 0; idx < models().size(); ++idx) + foreach_model([lmg0, this, &that](const std::string& str, ModelInfo& inf) { - if (this == lmg0 || !lmg0->linkEnabledModel(idx)->value()) + if (this == lmg0 || !lmg0->linkEnabledModel(str)->value()) { // try to load, if it fails, this will load a sane initial value - models()[idx].m_model->loadSettings(that, models()[idx].m_name); + inf.m_model->loadSettings(that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ } else { // model has the same value as in the first LinkedModelGroup } - } + }); } @@ -146,10 +160,10 @@ void LinkedModelGroup::loadValues(const QDomElement &that, void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) { - for (std::size_t i = 0; i < d.m_linkEnabled.size(); ++i) + foreach_model([&that](const std::string&, ModelInfo& inf) { - d.m_linkEnabled[i]->loadSettings(that, models()[i].m_name); - } + inf.m_linkEnabled->loadSettings(that, inf.m_name); + }); } @@ -157,7 +171,8 @@ void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) { - models().emplace_back(name, model); + model->setObjectName(name); + models().emplace(std::string(name.toUtf8().data()), ModelInfo(name, model)); } @@ -194,7 +209,7 @@ void LinkedModelGroups::createMultiChannelLinkModel() -void LinkedModelGroups::linkModel(std::size_t model, bool state) +void LinkedModelGroups::linkModel(const std::string& model, bool state) { LinkedModelGroup* first = getGroup(0); LinkedModelGroup* cur; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index c6ab433eac4..589a01a1abd 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -68,6 +68,7 @@ SET(LMMS_SRCS gui/widgets/LcdSpinBox.cpp gui/widgets/LcdWidget.cpp gui/widgets/LedCheckbox.cpp + gui/widgets/LinkedModelGroupLayout.cpp gui/widgets/LinkedModelGroupViews.cpp gui/widgets/MeterDialog.cpp gui/widgets/MidiPortMenu.cpp diff --git a/src/gui/widgets/LinkedModelGroupLayout.cpp b/src/gui/widgets/LinkedModelGroupLayout.cpp new file mode 100644 index 00000000000..7a0757efa73 --- /dev/null +++ b/src/gui/widgets/LinkedModelGroupLayout.cpp @@ -0,0 +1,305 @@ +/* + * LinkedModelGroups.cpp - implementation for LinkedModelGroups.h + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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. + * + */ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "LinkedModelGroupLayout.h" + +#include +#include +#include +#include +#include + +// TODO: rename: ModelGroupLayout + +constexpr const int LinkedModelGroupLayout::m_minWidth; + +LinkedModelGroupLayout::LinkedModelGroupLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing), + m_searchBar(new QLineEdit(parent)) +{ + setContentsMargins(margin, margin, margin, margin); + m_searchBar->setPlaceholderText("filter"); + connect(m_searchBar, SIGNAL(textChanged(const QString&)), + this, SLOT(onTextChanged(const QString& ))); + addWidget(m_searchBar); + m_searchBar->setHidden(true); // nothing to filter yet +} + +LinkedModelGroupLayout::~LinkedModelGroupLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) { delete item; } +} + +void LinkedModelGroupLayout::onTextChanged(const QString&) +{ + invalidate(); + update(); +} +#include +void LinkedModelGroupLayout::addItem(QLayoutItem *item) +{ + QWidget* widget = item->widget(); + const QString str = widget ? widget->objectName() : QString("unnamed"); + m_itemMap.insert(str, item); + // if there are at least two widget + search bar, show filter +// if(m_itemMap.size() > 2) { m_searchBar->setVisible(true); Q_ASSERT(false); qDebug() << "NO"; } + invalidate(); +} + +int LinkedModelGroupLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { return m_hSpace; } + else + { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int LinkedModelGroupLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { return m_vSpace; } + else + { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} + +int LinkedModelGroupLayout::count() const +{ + return m_itemMap.size() - 1; +} + +QMap::const_iterator +LinkedModelGroupLayout::pairAt(int index) const +{ + if(index < 0) + return m_itemMap.cend(); + QMap::const_iterator itr = m_itemMap.cbegin(); + ++itr; // skip search bar + while(index-->0 && itr != m_itemMap.cend()) { ++itr; } + return itr; +} + +// linear time :-( +QLayoutItem *LinkedModelGroupLayout::itemAt(int index) const +{ + auto itr = pairAt(index); + return itr == m_itemMap.end() ? nullptr : itr.value(); +} + +QLayoutItem *LinkedModelGroupLayout::itemByString(const QString &key) const +{ + auto itr = m_itemMap.find(key); + return (itr == m_itemMap.end()) ? nullptr : *itr; +} + +// linear time :-( +QLayoutItem *LinkedModelGroupLayout::takeAt(int index) +{ + auto itr = pairAt(index); + return (itr == m_itemMap.end()) ? nullptr : m_itemMap.take(itr.key()); +} + +Qt::Orientations LinkedModelGroupLayout::expandingDirections() const +{ + return Qt::Orientations(); +} + +bool LinkedModelGroupLayout::hasHeightForWidth() const +{ + return true; +} + +int LinkedModelGroupLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void LinkedModelGroupLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize LinkedModelGroupLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize LinkedModelGroupLayout::minimumSize() const +{ + // original formula from Qt's FlowLayout example: + // get maximum height and width for all children. + // as Qt will later call heightForWidth, only the width here really matters + QSize size; + for (const QLayoutItem *item : qAsConst(m_itemMap)) + { + size = size.expandedTo(item->minimumSize()); + } + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); + + // the original formula would leed to ~1 widget per row + // bash it at least to 400 so we have ~4 knobs per row + size.setWidth(qMax(size.width(), m_minWidth)); + return size; +} + +int LinkedModelGroupLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + const QString filterText = m_searchBar->text(); + bool first = true; + + QMapIterator itr(m_itemMap); + while (itr.hasNext()) + { + itr.next(); + QLayoutItem* item = itr.value(); + QWidget *wid = item->widget(); + + if (first || // do not filter search bar + filterText.isEmpty() || // no filter - pass all + itr.key().contains(filterText, Qt::CaseInsensitive)) + { + if(wid) + { + if(first) + { + // for the search bar, only show it if there are at least + // two control widgets (i.e. at least 3 widgets) + if(m_itemMap.size() > 2) { wid->show(); } + else { wid->hide(); } + } + else { wid->show(); } + + int spaceX = horizontalSpacing(); + if (spaceX == -1) + { + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + } + int spaceY = verticalSpacing(); + if (spaceY == -1) + { + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + } + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) + { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + { + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + first = false; + } + } + else + { + if(wid) { wid->hide(); } + } + } + return y + lineHeight - rect.y() + bottom; +} + +int LinkedModelGroupLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { return -1; } + else if (parent->isWidgetType()) + { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } + else { return static_cast(parent)->spacing(); } +} + + diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 188f18a1523..2ff02f928f9 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -28,6 +28,7 @@ #include "Controls.h" #include "LedCheckbox.h" +#include "LinkedModelGroupLayout.h" #include "LinkedModelGroups.h" #include "stdshims.h" @@ -40,10 +41,15 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, LinkedModelGroup *model, std::size_t colNum, std::size_t nProc, const QString& name) : QGroupBox(parent), + m_model(model), m_colNum(colNum), m_isLinking(model->isLinking()), - m_grid(new QGridLayout(this)) + m_layout(new LinkedModelGroupLayout(this)) { + // make viewable: if there are no knobs, the user should at least see + // a rectangle to drop controls in + setMinimumSize(QSize(50, 50)); + if (model->modelNum()) { std::size_t curProc = model->curProc(); @@ -67,7 +73,7 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, if (!chanName.isNull()) { setTitle(chanName); } } - else { setHidden(true); } + else { /*setHidden(true);*/ } } @@ -81,45 +87,81 @@ LinkedModelGroupView::~LinkedModelGroupView() {} void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) { // reconnect models - Q_ASSERT(m_controls.size() == group->modelNum()); - - for (std::size_t i = 0; i < group->modelNum(); ++i) + group->foreach_model([this](const std::string& str, + const LinkedModelGroup::ModelInfo& minf) { - m_controls[i]->setModel(group->model(i)); - } + auto itr = m_widgets.find(str); + // in case there are new or deleted widgets, the subclass has already + // modified m_widgets, so this will go into the else case + if(itr == m_widgets.end()) + { + // no widget? this can happen when the whole view is being destroyed + // (for some strange reasons) + } + else + { + itr->second.m_ctrl->setModel(minf.m_model); + if(itr->second.m_led) { + itr->second.m_led->setModel(minf.m_linkEnabled); + } + } + }); - for (std::size_t i = 0; i < m_leds.size(); ++i) - { - m_leds[i]->setModel(group->linkEnabledModel(i)); - } + m_model = group; } -void LinkedModelGroupView::addControl(Control* ctrl) +void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, + const std::string &display, bool removable) { - int colNum2 = static_cast(m_colNum * (1 + m_isLinking)); - int wdgNum = static_cast(m_controls.size() * (1 + m_isLinking)); + int wdgNum = static_cast(m_widgets.size() * (1 + m_isLinking)); if (ctrl) { - int x = wdgNum%colNum2, y = wdgNum/colNum2; + QWidget* box = new QWidget(this); + QHBoxLayout* boxLayout = new QHBoxLayout(box); + + // book-keeper of widget pointers + WidgetsPerModel widgets; - // start in row one, add widgets cell by cell if (m_isLinking) { - LedCheckBox* cb = new LedCheckBox( - ctrl->topWidget()->parentWidget()); - m_grid->addWidget(cb, y, x); - m_leds.push_back(std::unique_ptr(cb)); + widgets.m_led = new LedCheckBox(ctrl->topWidget()->parentWidget()); + boxLayout->addWidget(widgets.m_led); } - m_controls.push_back(std::unique_ptr(ctrl)); - m_grid->addWidget(ctrl->topWidget(), y, x + 1, Qt::AlignCenter); + widgets.m_ctrl.reset(ctrl); + boxLayout->addWidget(ctrl->topWidget()); + + if (removable) + { + QPushButton* removeBtn = new QPushButton; + removeBtn->setIcon( embed::getIconPixmap( "discard" ) ); + QObject::connect(removeBtn, &QPushButton::clicked, + this, [this,ctrl](bool){ + AutomatableModel* controlModel = ctrl->model(); + // remove control out of model group + // (will also remove it from the UI) + m_model->removeControl(controlModel); + // delete model (includes disconnecting all connections) + delete controlModel; + }, + Qt::DirectConnection); + boxLayout->addWidget(removeBtn); + } + + // required, so the Layout knows how to sort/filter widgets by string + box->setObjectName(QString::fromStdString(display)); + m_layout->addWidget(box); + + m_widgets.emplace(id, std::move(widgets)); // TODO: use set? wdgNum += m_isLinking; ++wdgNum; } + if(isHidden()) { setHidden(false); } + // matter of taste (takes a lot of space): // makeAllGridCellsEqualSized(); } @@ -127,15 +169,40 @@ void LinkedModelGroupView::addControl(Control* ctrl) +void LinkedModelGroupView::removeControl(const QString& key) +{ + auto itr = m_widgets.find(key.toStdString()); + if(itr != m_widgets.end()) + { + QLayoutItem* item = m_layout->itemByString(key); + Q_ASSERT(!!item); + QWidget* wdg = item->widget(); + Q_ASSERT(!!wdg); + + // remove item from layout + m_layout->removeItem(item); + // the widget still exists and is visible - remove it now + delete wdg; + // erase widget pointer from dictionary + m_widgets.erase(itr); + // repaint immediately, so we don't have dangling model views + m_layout->update(); + } +} + + + + void LinkedModelGroupView::makeAllGridCellsEqualSized() { +#if 0 int rowHeight = 0, colWidth = 0; - for (int row = 0; row < m_grid->rowCount(); ++row) + for (int row = 0; row < m_layout->rowCount(); ++row) { - for (int col = 0; col < m_grid->columnCount(); ++col) + for (int col = 0; col < m_layout->columnCount(); ++col) { QLayoutItem* layout; - if ((layout = m_grid->itemAtPosition(row, col))) + if ((layout = m_layout->itemAtPosition(row, col))) { rowHeight = qMax(rowHeight, layout->geometry().height()); colWidth = qMax(colWidth, layout->geometry().width()); @@ -143,15 +210,16 @@ void LinkedModelGroupView::makeAllGridCellsEqualSized() } } - for (int row = 0; row < m_grid->rowCount(); ++row) + for (int row = 0; row < m_layout->rowCount(); ++row) { - m_grid->setRowMinimumHeight(row, rowHeight); + m_layout->setRowMinimumHeight(row, rowHeight); } - for (int col = 0; col < m_grid->columnCount(); ++col) + for (int col = 0; col < m_layout->columnCount(); ++col) { - m_grid->setColumnMinimumWidth(col, colWidth); + m_layout->setColumnMinimumWidth(col, colWidth); } +#endif } From 5955e37210f3f375777b2c8fbc1b134c327ea1c5 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 5 Dec 2019 20:40:30 +0100 Subject: [PATCH 053/120] LinkedModelGroups: Always keep groups linked Before this commit, each group had its controls, and they were independent. This commit now forces that all groups are constantly linked, i.e. there remains only one value associated controls can have (#5079). --- include/LinkedModelGroupViews.h | 1 - include/LinkedModelGroups.h | 90 +-------- src/core/LinkedModelGroups.cpp | 236 ++-------------------- src/gui/widgets/LinkedModelGroupViews.cpp | 22 -- 4 files changed, 22 insertions(+), 327 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index dad24ba5217..23c6f62c1cd 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -99,7 +99,6 @@ class LinkedModelGroupView : public QGroupBox class LinkedModelGroupsView { protected: - LinkedModelGroupsView(class LinkedModelGroups *ctrlBase); ~LinkedModelGroupsView() = default; //! Reconnect models if model changed; to be called by child virtuals diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index f328d0a61db..eb26bf14dc0 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -46,11 +46,6 @@ */ class LinkedModelGroup : public Model { - Q_OBJECT -signals: - //! Signal emitted after any of the per-control link-enabled models switch - void linkStateChanged(const std::string& str, bool value); - public: /* Initialization @@ -60,23 +55,11 @@ class LinkedModelGroup : public Model //! @param nProc total number of processors LinkedModelGroup(Model* parent, std::size_t curProc) : Model(parent), m_curProc(curProc) {} - //! After all models have been added, make this processor the one which - //! will contain link models associated with its controls - void makeLinkingProc(); /* Linking */ - //! Set all per-control link-enabled models to @p state, which will - //! also link or unlink them (via `LinkedModelGroups::linkModel()`) - void linkAllModels(bool state); - //! Link specified port with the associated port of @p other - //! @param id string identifier of the port - void linkControls(LinkedModelGroup* other, const std::string& id); - //! @see linkControls - void unlinkControls(LinkedModelGroup *other, const std::string &id); - //! Return whether this is the first of more than one processors - bool isLinking() const; + void linkControls(LinkedModelGroup *other); /* Models @@ -85,45 +68,11 @@ class LinkedModelGroup : public Model { QString m_name; class AutomatableModel* m_model; - class BoolModel* m_linkEnabled = nullptr; ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? ModelInfo(const QString& name, AutomatableModel* model) : m_name(name), m_model(model) {} }; - // TODO: refactor those 4 - AutomatableModel* model(const std::string& s) - { - auto itr = d.m_models.find(s); - if(itr == d.m_models.end()) - throw std::runtime_error("..."); - return itr->second.m_model; - } - - const AutomatableModel* model(const std::string& s) const - { - auto itr = d.m_models.find(s); - if(itr == d.m_models.end()) - throw std::runtime_error("..."); - return itr->second.m_model; - } - - class BoolModel* linkEnabledModel(const std::string& s) - { - auto itr = d.m_models.find(s); - if(itr == d.m_models.end()) - throw std::runtime_error("..."); - return itr->second.m_linkEnabled; - } - - const class BoolModel* linkEnabledModel(const std::string& s) const - { - auto itr = d.m_models.find(s); - if(itr == d.m_models.end()) - throw std::runtime_error("..."); - return itr->second.m_linkEnabled; - } - template void foreach_model(const Functor& ftor) { @@ -142,7 +91,6 @@ class LinkedModelGroup : public Model } } - AutomatableModel* modelWithName(const QString& name) const; std::size_t modelNum() const { return models().size(); } // this is bad style (redirecting into the sub-class), but this class @@ -154,12 +102,9 @@ class LinkedModelGroup : public Model Load/Save */ //! @param lmg0 The linking model group with index 0 - void saveValues(class QDomDocument& doc, class QDomElement& that, - const LinkedModelGroup *lmg0); - void saveLinksEnabled(QDomDocument &doc, QDomElement &that); + void saveValues(class QDomDocument& doc, class QDomElement& that); //! @param lmg0 The linking model group with index 0 - void loadValues(const class QDomElement& that, const LinkedModelGroup *lmg0); - void loadLinksEnabled(const class QDomElement &that); + void loadValues(const class QDomElement& that); /* General @@ -217,22 +162,7 @@ class LinkedModelGroups public: virtual ~LinkedModelGroups(); - //! Return the model for multi channel linking - BoolModel* multiChannelLinkModel() { return m_multiChannelLinkModel.get(); } - //! Create the model for multi channel linking - void createMultiChannelLinkModel(); - - /* - to be called by slots - */ - //! Take a specified model from the first LinkedModelGroup - //! and link or unlink it to/from the associated model - //! of every other LinkedModelGroup - //! @param model number conforming to getGroup() - //! @param state True iff it should be linked - void linkModel(const std::string &model, bool state); - //! Callback for the global linking LED - void updateLinkStatesFromGlobal(); + void linkAllModels(); /* Load/Save @@ -248,18 +178,6 @@ class LinkedModelGroups virtual LinkedModelGroup* getGroup(std::size_t idx) = 0; //! @see getGroup virtual const LinkedModelGroup* getGroup(std::size_t idx) const = 0; - -private: - // Implement deletion in the CPP file: - struct BoolModelDeleter { void operator()(class BoolModel* l); }; - - //! Model for the "global" linking - //! Only allocated if #processors > 1 - std::unique_ptr m_multiChannelLinkModel; - - //! Force updateLinkStatesFromGlobal() to not unlink any ports - //! Implementation detail, see linkPort() implementation - bool m_noLink = false; }; diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 1cc40dab0cf..65d73725bc2 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -38,131 +38,36 @@ */ - - -void LinkedModelGroup::makeLinkingProc() +void LinkedModelGroup::linkControls(LinkedModelGroup *other) { - foreach_model([this](const std::string& str, ModelInfo inf) + foreach_model([&other](const std::string& id, ModelInfo& inf) { - BoolModel* bmo = new BoolModel(true, this, tr("Link channels")); - inf.m_linkEnabled = bmo; - connect(bmo, &BoolModel::dataChanged, this, - [this, bmo, str]() { emit linkStateChanged(str, bmo->value()); }); + auto itr2 = other->models().find(id); + Q_ASSERT(itr2 != other->models().end()); + AutomatableModel::linkModels(inf.m_model, itr2->second.m_model); }); } -void LinkedModelGroup::linkAllModels(bool state) -{ - // this will call AutomatableModel::linkModels (??? TODO) - for (auto pr : d.m_models) { pr.second.m_linkEnabled->setValue(state); } -} - - - - -void LinkedModelGroup::linkControls(LinkedModelGroup *other, const std::string& id) -{ - auto itr1 = models().find(id); - auto itr2 = other->models().find(id); - Q_ASSERT(itr1 != models().end() && itr2 != other->models().end()); - AutomatableModel::linkModels(itr1->second.m_model, itr2->second.m_model); -} - - - - -void LinkedModelGroup::unlinkControls(LinkedModelGroup *other, const std::string& id) -{ - auto itr1 = models().find(id); - auto itr2 = other->models().find(id); - Q_ASSERT(itr1 != models().end() && itr2 != other->models().end()); - AutomatableModel::unlinkModels(itr1->second.m_model, itr2->second.m_model); -} - - - - -bool LinkedModelGroup::isLinking() const { - return d.m_models.size() - ?!!d.m_models.begin()->second.m_linkEnabled : false; -} - - - - -AutomatableModel *LinkedModelGroup::modelWithName(const QString &name) const +void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) { - auto itr = std::find_if(models().begin(), models().end(), - [&name](const std::pair& pr) -> bool - { return pr.second.m_name == name; }); - return itr == models().end() ? nullptr : itr->second.m_model; -} - - - - -void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that, - const LinkedModelGroup *lmg0) -{ - // if multiple lmgs, the first one must currently be the linking one - Q_ASSERT(this == lmg0 || lmg0->isLinking()); - foreach_model([&lmg0, this, &doc, &that](const std::string& str, ModelInfo& inf) + foreach_model([&doc, &that](const std::string& , ModelInfo& inf) { - if (this == lmg0 || !lmg0->linkEnabledModel(str)->value()) - { - // try to load, if it fails, this will load a sane initial value - inf.m_model->saveSettings(doc, that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ - } - else - { - // model has the same value as in the first LinkedModelGroup - } + inf.m_model->saveSettings(doc, that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ }); } -void LinkedModelGroup::saveLinksEnabled(QDomDocument &doc, QDomElement &that) +void LinkedModelGroup::loadValues(const QDomElement &that) { - //for (std::size_t i = 0; i < d.m_linkEnabled.size(); ++i) - foreach_model([&doc, &that](const std::string&, ModelInfo& inf) + foreach_model([&that](const std::string& , ModelInfo& inf) { - inf.m_linkEnabled->saveSettings(doc, that, inf.m_name); - }); -} - - - - -void LinkedModelGroup::loadValues(const QDomElement &that, - const LinkedModelGroup* lmg0) -{ - foreach_model([lmg0, this, &that](const std::string& str, ModelInfo& inf) - { - if (this == lmg0 || !lmg0->linkEnabledModel(str)->value()) - { - // try to load, if it fails, this will load a sane initial value - inf.m_model->loadSettings(that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ - } - else - { - // model has the same value as in the first LinkedModelGroup - } - }); -} - - - - -void LinkedModelGroup::loadLinksEnabled(const QDomElement &that) -{ - foreach_model([&that](const std::string&, ModelInfo& inf) - { - inf.m_linkEnabled->loadSettings(that, inf.m_name); + // try to load, if it fails, this will load a sane initial value + inf.m_model->loadSettings(that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ }); } @@ -194,105 +99,33 @@ void LinkedModelGroup::clearModels() - LinkedModelGroups::~LinkedModelGroups() {} -void LinkedModelGroups::createMultiChannelLinkModel() -{ - m_multiChannelLinkModel = - make_unique(true, nullptr); -} - - - - -void LinkedModelGroups::linkModel(const std::string& model, bool state) +void LinkedModelGroups::linkAllModels() { LinkedModelGroup* first = getGroup(0); LinkedModelGroup* cur; - if (state) - { - for (std::size_t i = 1; (cur = getGroup(i)); ++i) - { - first->linkControls(cur, model); - } - } - else + for (std::size_t i = 1; (cur = getGroup(i)); ++i) { - for (std::size_t i = 1; (cur = getGroup(i)); ++i) - { - first->unlinkControls(cur, model); - } - - // m_multiChannelLinkModel.setValue() will call - // updateLinkStatesFromGlobal()... - // m_noLink will make sure that this will not unlink any other models - m_noLink = true; - m_multiChannelLinkModel->setValue( false ); + first->linkControls(cur); } } -void LinkedModelGroups::updateLinkStatesFromGlobal() -{ - LinkedModelGroup* first = getGroup(0); - if (m_multiChannelLinkModel->value()) - { - first->linkAllModels(true); - } - else if (!m_noLink) - { - first->linkAllModels(false); - } - - m_noLink = false; -} - - - - void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) { LinkedModelGroup* grp0 = getGroup(0); if (grp0) { - bool allLinked = false; - if (m_multiChannelLinkModel) - { - m_multiChannelLinkModel->saveSettings(doc, that, "link"); - allLinked = m_multiChannelLinkModel->value(); - } - - if (!allLinked && getGroup(1)) - { - QDomElement links = doc.createElement("links"); - grp0->saveLinksEnabled(doc, links); - that.appendChild(links); - } - QDomElement models = doc.createElement("models"); that.appendChild(models); - - char chanName[] = "chan0"; - LinkedModelGroup* lmg; - for (std::size_t chanIdx = 0; - // stop after last group - // if all models are linked, store only the first group - (lmg = getGroup(chanIdx)) && !(allLinked && chanIdx > 0); - ++chanIdx) - { - chanName[4] = '0' + static_cast(chanIdx); - QDomElement channel = doc.createElement( - QString::fromUtf8(chanName)); - models.appendChild(channel); - lmg->saveValues(doc, channel, grp0); - } + grp0->saveValues(doc, models); } else { /* don't even add a "models" node */ } } @@ -306,41 +139,8 @@ void LinkedModelGroups::loadSettings(const QDomElement& that) LinkedModelGroup* grp0; if (!models.isNull() && (grp0 = getGroup(0))) { - bool allLinked = false; - if (m_multiChannelLinkModel) - { - m_multiChannelLinkModel->loadSettings(that, "link"); - allLinked = m_multiChannelLinkModel->value(); - } - - if (!allLinked && getGroup(1)) - { - QDomElement links = that.firstChildElement("links"); - if (!links.isNull()) { grp0->loadLinksEnabled(links); } - } - - QDomElement lastChan; - char chanName[] = "chan0"; - LinkedModelGroup* lmg; - for (std::size_t chanIdx = 0; - // stop after last group - // if all models are linked, read only the first group - (lmg = getGroup(chanIdx)) && !(allLinked && chanIdx > 0); - ++chanIdx) - { - chanName[4] = '0' + static_cast(chanIdx); - QDomElement chan = models.firstChildElement(chanName); - if (!chan.isNull()) { lastChan = chan; } - - lmg->loadValues(lastChan, grp0); - } + // only load the first group, the others are linked to the first + grp0->loadValues(models); } } - - - -void LinkedModelGroups::BoolModelDeleter::operator()(BoolModel *l) -{ - delete l; -} diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 2ff02f928f9..3ed8eec02c4 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -43,7 +43,6 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, QGroupBox(parent), m_model(model), m_colNum(colNum), - m_isLinking(model->isLinking()), m_layout(new LinkedModelGroupLayout(this)) { // make viewable: if there are no knobs, the user should at least see @@ -101,9 +100,6 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) else { itr->second.m_ctrl->setModel(minf.m_model); - if(itr->second.m_led) { - itr->second.m_led->setModel(minf.m_linkEnabled); - } } }); @@ -228,26 +224,8 @@ void LinkedModelGroupView::makeAllGridCellsEqualSized() */ -LinkedModelGroupsView::LinkedModelGroupsView( - LinkedModelGroups *ctrlBase) -{ - if (ctrlBase->multiChannelLinkModel()) - { - m_multiChannelLink = make_unique - (QObject::tr("Link Channels"), nullptr); - } -} - - - - void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) { - if (groups->multiChannelLinkModel()) - { - m_multiChannelLink->setModel(groups->multiChannelLinkModel()); - } - LinkedModelGroupView* groupView; LinkedModelGroup* group; for (std::size_t i = 0; From d0b7dc1dc1e96843cf036129bc4275baedfabbe1 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Dec 2019 10:00:17 +0100 Subject: [PATCH 054/120] Remove linking LEDs Since HEAD~1, all LEDs are fixed linked by default --- include/LinkedModelGroupViews.h | 10 ---------- src/gui/widgets/LinkedModelGroupViews.cpp | 17 +---------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 23c6f62c1cd..82731f398ff 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -77,12 +77,10 @@ class LinkedModelGroupView : public QGroupBox //! column number in surrounding grid in LinkedModelGroupsView std::size_t m_colNum; - bool m_isLinking; class LinkedModelGroupLayout* m_layout; struct WidgetsPerModel { std::unique_ptr m_ctrl; - class LedCheckBox* m_led = nullptr; }; std::map m_widgets; @@ -104,18 +102,10 @@ class LinkedModelGroupsView //! Reconnect models if model changed; to be called by child virtuals void modelChanged(class LinkedModelGroups* ctrlBase); - //! Access to the global multi channel link LED - LedCheckBox* globalLinkLed() { return m_multiChannelLink.get(); } - private: //! The base class must return the adressed group view, or nullptr if index //! is out of range virtual LinkedModelGroupView* getGroupView(std::size_t idx) = 0; - - // Implement deletion in the CPP file: - struct MultiChannelLinkDeleter { void operator()(LedCheckBox* l); }; - std::unique_ptr - m_multiChannelLink = nullptr; }; diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 3ed8eec02c4..b56db8b8af8 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -112,7 +112,7 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, const std::string &display, bool removable) { - int wdgNum = static_cast(m_widgets.size() * (1 + m_isLinking)); + int wdgNum = static_cast(m_widgets.size()); if (ctrl) { QWidget* box = new QWidget(this); @@ -121,12 +121,6 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, // book-keeper of widget pointers WidgetsPerModel widgets; - if (m_isLinking) - { - widgets.m_led = new LedCheckBox(ctrl->topWidget()->parentWidget()); - boxLayout->addWidget(widgets.m_led); - } - widgets.m_ctrl.reset(ctrl); boxLayout->addWidget(ctrl->topWidget()); @@ -152,7 +146,6 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, m_layout->addWidget(box); m_widgets.emplace(id, std::move(widgets)); // TODO: use set? - wdgNum += m_isLinking; ++wdgNum; } @@ -237,11 +230,3 @@ void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) } - - -// If you wonder why the default deleter can not be used: -// https://stackoverflow.com/questions/9954518 -void LinkedModelGroupsView::MultiChannelLinkDeleter:: - operator()(LedCheckBox *l) { delete l; } - - From 94ea93ebdc315bab48e82c127c60b7ce70494581 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Dec 2019 19:04:28 +0100 Subject: [PATCH 055/120] Show only one processor view This is fine as all processors have linked controls - the proc views would all show the same. --- include/LinkedModelGroupViews.h | 8 ++--- src/gui/widgets/LinkedModelGroupViews.cpp | 39 ++++------------------- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 82731f398ff..c4b554d010a 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include /** @@ -46,7 +46,7 @@ ModelView. The "view" in the name is just for consistency with LinkedModelGroupsView. */ -class LinkedModelGroupView : public QGroupBox +class LinkedModelGroupView : public QWidget { public: /** @@ -56,7 +56,7 @@ class LinkedModelGroupView : public QGroupBox automatically set if not given */ LinkedModelGroupView(QWidget *parent, class LinkedModelGroup* model, - std::size_t colNum, std::size_t nProc, const QString &name = QString()); + std::size_t colNum); ~LinkedModelGroupView(); //! Reconnect models if model changed @@ -105,7 +105,7 @@ class LinkedModelGroupsView private: //! The base class must return the adressed group view, or nullptr if index //! is out of range - virtual LinkedModelGroupView* getGroupView(std::size_t idx) = 0; + virtual LinkedModelGroupView* getGroupView() = 0; }; diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index b56db8b8af8..8cb8e70d5d8 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -39,8 +39,8 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, - LinkedModelGroup *model, std::size_t colNum, std::size_t nProc, const QString& name) : - QGroupBox(parent), + LinkedModelGroup *model, std::size_t colNum) : + QWidget(parent), m_model(model), m_colNum(colNum), m_layout(new LinkedModelGroupLayout(this)) @@ -48,31 +48,6 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, // make viewable: if there are no knobs, the user should at least see // a rectangle to drop controls in setMinimumSize(QSize(50, 50)); - - if (model->modelNum()) - { - std::size_t curProc = model->curProc(); - QString chanName; - if (name.isNull()) - { - switch (nProc) - { - case 1: break; // don't display any channel name - case 2: - chanName = curProc - ? QObject::tr("Right") - : QObject::tr("Left"); - break; - default: - chanName = QObject::tr("Channel %1").arg(curProc + 1); - break; - } - } - else { chanName = name; } - - if (!chanName.isNull()) { setTitle(chanName); } - } - else { /*setHidden(true);*/ } } @@ -219,13 +194,11 @@ void LinkedModelGroupView::makeAllGridCellsEqualSized() void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) { - LinkedModelGroupView* groupView; - LinkedModelGroup* group; - for (std::size_t i = 0; - (group = groups->getGroup(i)) && (groupView = getGroupView(i)); - ++i) + LinkedModelGroupView* groupView = getGroupView(); + LinkedModelGroup* group0 = groups->getGroup(0); + if(group0 && groupView) { - groupView->modelChanged(group); + groupView->modelChanged(group0); } } From 74ad422dafe9e8eb3fa609934d193774f36f2eb6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 10 Dec 2019 06:55:13 +0100 Subject: [PATCH 056/120] Minor cleanups, doc cleanups --- include/LinkedModelGroupViews.h | 21 +++----- include/LinkedModelGroups.h | 64 +++++++---------------- src/core/LinkedModelGroups.cpp | 16 +++--- src/gui/widgets/LinkedModelGroupViews.cpp | 47 ++--------------- 4 files changed, 37 insertions(+), 111 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index c4b554d010a..ed9870ccf36 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -1,5 +1,5 @@ /* - * LinkedModelGroupViews.h - views for groups of linkable models + * LinkedModelGroupViews.h - view for groups of linkable models * * Copyright (c) 2019-2019 Johannes Lorenz * @@ -39,8 +39,7 @@ /** - View for one processor, LinkedModelGroupsViewBase contains 2 - of those for mono plugins. + View for a representative processor @note Neither this class, nor any inheriting classes, shall inherit ModelView. The "view" in the name is just for consistency @@ -52,8 +51,6 @@ class LinkedModelGroupView : public QWidget /** @param colNum numbers of columns for the controls (link LEDs not counted) - @param name Name for the group, like "Left" or "Group 1", - automatically set if not given */ LinkedModelGroupView(QWidget *parent, class LinkedModelGroup* model, std::size_t colNum); @@ -78,18 +75,12 @@ class LinkedModelGroupView : public QWidget //! column number in surrounding grid in LinkedModelGroupsView std::size_t m_colNum; class LinkedModelGroupLayout* m_layout; - struct WidgetsPerModel - { - std::unique_ptr m_ctrl; - }; - - std::map m_widgets; + std::map> m_widgets; }; /** - Base class for view for one plugin with linkable models. - Provides a global channel link LED. + Container class for one LinkedModelGroupView @note It's intended this class does not inherit from ModelView. Inheriting classes need to do that, see e.g. Lv2Instrument.h @@ -103,8 +94,8 @@ class LinkedModelGroupsView void modelChanged(class LinkedModelGroups* ctrlBase); private: - //! The base class must return the adressed group view, or nullptr if index - //! is out of range + //! The base class must return the adressed group view, + //! which has the same value as "this" virtual LinkedModelGroupView* getGroupView() = 0; }; diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index eb26bf14dc0..42880e9c38d 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -1,5 +1,5 @@ /* - * LinkedModelGroups.h - base classes for groups of linkable models + * LinkedModelGroups.h - base classes for groups of linked models * * Copyright (c) 2019-2019 Johannes Lorenz * @@ -40,9 +40,13 @@ /** - Base class for a group of linkable models + Base class for a group of linked models See the LinkedModelGroup class for explenations + + Features: + * Models are stored by their QObject::objectName + * Models are linked automatically */ class LinkedModelGroup : public Model { @@ -51,13 +55,10 @@ class LinkedModelGroup : public Model Initialization */ //! @param parent model of the LinkedModelGroups class - //! @param curProc number of this processor, counted from 0 - //! @param nProc total number of processors - LinkedModelGroup(Model* parent, std::size_t curProc) : - Model(parent), m_curProc(curProc) {} + LinkedModelGroup(Model* parent) : Model(parent) {} /* - Linking + Linking (initially only) */ void linkControls(LinkedModelGroup *other); @@ -73,10 +74,11 @@ class LinkedModelGroup : public Model : m_name(name), m_model(model) {} }; + // TODO: refactor those 2 template void foreach_model(const Functor& ftor) { - for(auto itr = d.m_models.begin(); itr != d.m_models.end(); ++itr) + for(auto itr = m_models.begin(); itr != m_models.end(); ++itr) { ftor(itr->first, itr->second); } @@ -85,13 +87,13 @@ class LinkedModelGroup : public Model template void foreach_model(const Functor& ftor) const { - for(auto itr = d.m_models.cbegin(); itr != d.m_models.cend(); ++itr) + for(auto itr = m_models.cbegin(); itr != m_models.cend(); ++itr) { ftor(itr->first, itr->second); } } - std::size_t modelNum() const { return models().size(); } + std::size_t modelNum() const { return m_models.size(); } // this is bad style (redirecting into the sub-class), but this class // will be married with the sub-classes (Lv2Proc, SpaProc) anyways, @@ -101,58 +103,32 @@ class LinkedModelGroup : public Model /* Load/Save */ - //! @param lmg0 The linking model group with index 0 void saveValues(class QDomDocument& doc, class QDomElement& that); - //! @param lmg0 The linking model group with index 0 void loadValues(const class QDomElement& that); - /* - General - */ - std::size_t curProc() const { return m_curProc; } - protected: //! Register a further model void addModel(class AutomatableModel* model, const QString& name); - //! Remove all models and all link-enabled models + //! Remove all models void clearModels(); private: - // TODO: remove - std::map& models() { return d.m_models; } - const std::map& models() const { return d.m_models; } - - struct - { - //! models for the controls; the vector defines indices for the controls - std::map m_models; - } d; - - std::size_t m_curProc; + //! models for the controls + std::map m_models; }; /** - Container for multiple equal groups of linked models + Container for a group of linked models Each group contains the same models and model types. The models are - numbered, and equal numbered models are associated and can be linked. + numbered, and equal numbered models are associated and always linked. A typical application are two mono plugins making a stereo plugin. - Inheriting classes need to do the following connections: - - \code - if (multiChannelLinkModel()) - { - connect(multiChannelLinkModel(), &BoolModel::dataChanged, - this, [this](){updateLinkStatesFromGlobal();}, - Qt::DirectConnection); - connect(getGroup(0), &LinkedModelGroup::linkStateChanged, - this, [this](std::size_t id, bool value){ - linkModel(id, value);}, Qt::DirectConnection); - } - \endcode + @note Though this class can contain multiple model groups, a corresponding + view ("LinkedModelGroupViews") will only display one group, as they all have + the same values @note Though called "container", this class does not contain, but only know the single groups. The inheriting classes are responsible for storage. diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 65d73725bc2..87420b83799 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -1,5 +1,5 @@ /* - * LinkedModelGroups.cpp - base classes for groups of linkable models + * LinkedModelGroups.cpp - base classes for groups of linked models * * Copyright (c) 2019-2019 Johannes Lorenz * @@ -42,8 +42,8 @@ void LinkedModelGroup::linkControls(LinkedModelGroup *other) { foreach_model([&other](const std::string& id, ModelInfo& inf) { - auto itr2 = other->models().find(id); - Q_ASSERT(itr2 != other->models().end()); + auto itr2 = other->m_models.find(id); + Q_ASSERT(itr2 != other->m_models.end()); AutomatableModel::linkModels(inf.m_model, itr2->second.m_model); }); } @@ -55,7 +55,7 @@ void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) { foreach_model([&doc, &that](const std::string& , ModelInfo& inf) { - inf.m_model->saveSettings(doc, that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ + inf.m_model->saveSettings(doc, that, /*m_models[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ }); } @@ -67,7 +67,7 @@ void LinkedModelGroup::loadValues(const QDomElement &that) foreach_model([&that](const std::string& , ModelInfo& inf) { // try to load, if it fails, this will load a sane initial value - inf.m_model->loadSettings(that, /*models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ + inf.m_model->loadSettings(that, /*m_models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ }); } @@ -77,7 +77,7 @@ void LinkedModelGroup::loadValues(const QDomElement &that) void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) { model->setObjectName(name); - models().emplace(std::string(name.toUtf8().data()), ModelInfo(name, model)); + m_models.emplace(std::string(name.toUtf8().data()), ModelInfo(name, model)); } @@ -85,9 +85,7 @@ void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) void LinkedModelGroup::clearModels() { - using datatype = decltype(d); - d.~datatype(); - new (&d) datatype(); + m_models.clear(); } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 8cb8e70d5d8..82d29534c57 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -1,5 +1,5 @@ /* - * LinkedModelGroupViews.h - views for groups of linkable models + * LinkedModelGroupViews.h - view for groups of linkable models * * Copyright (c) 2019-2019 Johannes Lorenz * @@ -74,7 +74,7 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) } else { - itr->second.m_ctrl->setModel(minf.m_model); + itr->second->setModel(minf.m_model); } }); @@ -93,10 +93,6 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, QWidget* box = new QWidget(this); QHBoxLayout* boxLayout = new QHBoxLayout(box); - // book-keeper of widget pointers - WidgetsPerModel widgets; - - widgets.m_ctrl.reset(ctrl); boxLayout->addWidget(ctrl->topWidget()); if (removable) @@ -120,14 +116,11 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, box->setObjectName(QString::fromStdString(display)); m_layout->addWidget(box); - m_widgets.emplace(id, std::move(widgets)); // TODO: use set? + m_widgets.emplace(id, ctrl); // TODO: use set? ++wdgNum; } - if(isHidden()) { setHidden(false); } - - // matter of taste (takes a lot of space): - // makeAllGridCellsEqualSized(); + if(isHidden()) { setHidden(false); } // TODO: can be removed? } @@ -155,38 +148,6 @@ void LinkedModelGroupView::removeControl(const QString& key) } - - -void LinkedModelGroupView::makeAllGridCellsEqualSized() -{ -#if 0 - int rowHeight = 0, colWidth = 0; - for (int row = 0; row < m_layout->rowCount(); ++row) - { - for (int col = 0; col < m_layout->columnCount(); ++col) - { - QLayoutItem* layout; - if ((layout = m_layout->itemAtPosition(row, col))) - { - rowHeight = qMax(rowHeight, layout->geometry().height()); - colWidth = qMax(colWidth, layout->geometry().width()); - } - } - } - - for (int row = 0; row < m_layout->rowCount(); ++row) - { - m_layout->setRowMinimumHeight(row, rowHeight); - } - - for (int col = 0; col < m_layout->columnCount(); ++col) - { - m_layout->setColumnMinimumWidth(col, colWidth); - } -#endif -} - - /* LinkedModelGroupsViewBase */ From 68777efe3c3475aa9f7e26cf93782920cbc5548b Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 10 Dec 2019 15:24:00 +0100 Subject: [PATCH 057/120] Whitespace or non-functional fixes --- include/LinkedModelGroupViews.h | 2 -- include/LinkedModelGroups.h | 4 ++-- src/gui/widgets/LinkedModelGroupLayout.cpp | 16 +++++++--------- src/gui/widgets/LinkedModelGroupViews.cpp | 16 ++++++---------- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index ed9870ccf36..94f416333fe 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -68,8 +68,6 @@ class LinkedModelGroupView : public QWidget void removeControl(const QString &key); private: - void makeAllGridCellsEqualSized(); - class LinkedModelGroup* m_model; //! column number in surrounding grid in LinkedModelGroupsView diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 42880e9c38d..87013322464 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -78,7 +78,7 @@ class LinkedModelGroup : public Model template void foreach_model(const Functor& ftor) { - for(auto itr = m_models.begin(); itr != m_models.end(); ++itr) + for (auto itr = m_models.begin(); itr != m_models.end(); ++itr) { ftor(itr->first, itr->second); } @@ -87,7 +87,7 @@ class LinkedModelGroup : public Model template void foreach_model(const Functor& ftor) const { - for(auto itr = m_models.cbegin(); itr != m_models.cend(); ++itr) + for (auto itr = m_models.cbegin(); itr != m_models.cend(); ++itr) { ftor(itr->first, itr->second); } diff --git a/src/gui/widgets/LinkedModelGroupLayout.cpp b/src/gui/widgets/LinkedModelGroupLayout.cpp index 7a0757efa73..fe935c0123e 100644 --- a/src/gui/widgets/LinkedModelGroupLayout.cpp +++ b/src/gui/widgets/LinkedModelGroupLayout.cpp @@ -106,14 +106,12 @@ void LinkedModelGroupLayout::onTextChanged(const QString&) invalidate(); update(); } -#include + void LinkedModelGroupLayout::addItem(QLayoutItem *item) { QWidget* widget = item->widget(); const QString str = widget ? widget->objectName() : QString("unnamed"); m_itemMap.insert(str, item); - // if there are at least two widget + search bar, show filter -// if(m_itemMap.size() > 2) { m_searchBar->setVisible(true); Q_ASSERT(false); qDebug() << "NO"; } invalidate(); } @@ -143,11 +141,11 @@ int LinkedModelGroupLayout::count() const QMap::const_iterator LinkedModelGroupLayout::pairAt(int index) const { - if(index < 0) + if (index < 0) return m_itemMap.cend(); QMap::const_iterator itr = m_itemMap.cbegin(); ++itr; // skip search bar - while(index-->0 && itr != m_itemMap.cend()) { ++itr; } + while (index-->0 && itr != m_itemMap.cend()) { ++itr; } return itr; } @@ -240,13 +238,13 @@ int LinkedModelGroupLayout::doLayout(const QRect &rect, bool testOnly) const filterText.isEmpty() || // no filter - pass all itr.key().contains(filterText, Qt::CaseInsensitive)) { - if(wid) + if (wid) { - if(first) + if (first) { // for the search bar, only show it if there are at least // two control widgets (i.e. at least 3 widgets) - if(m_itemMap.size() > 2) { wid->show(); } + if (m_itemMap.size() > 2) { wid->show(); } else { wid->hide(); } } else { wid->show(); } @@ -284,7 +282,7 @@ int LinkedModelGroupLayout::doLayout(const QRect &rect, bool testOnly) const } else { - if(wid) { wid->hide(); } + if (wid) { wid->hide(); } } } return y + lineHeight - rect.y() + bottom; diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 82d29534c57..46f6bc4e332 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -24,13 +24,10 @@ #include "LinkedModelGroupViews.h" -#include - +#include #include "Controls.h" -#include "LedCheckbox.h" #include "LinkedModelGroupLayout.h" #include "LinkedModelGroups.h" -#include "stdshims.h" /* @@ -67,7 +64,7 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) auto itr = m_widgets.find(str); // in case there are new or deleted widgets, the subclass has already // modified m_widgets, so this will go into the else case - if(itr == m_widgets.end()) + if (itr == m_widgets.end()) { // no widget? this can happen when the whole view is being destroyed // (for some strange reasons) @@ -92,7 +89,6 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, { QWidget* box = new QWidget(this); QHBoxLayout* boxLayout = new QHBoxLayout(box); - boxLayout->addWidget(ctrl->topWidget()); if (removable) @@ -116,11 +112,11 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, box->setObjectName(QString::fromStdString(display)); m_layout->addWidget(box); - m_widgets.emplace(id, ctrl); // TODO: use set? + m_widgets.emplace(id, ctrl); ++wdgNum; } - if(isHidden()) { setHidden(false); } // TODO: can be removed? + if (isHidden()) { setHidden(false); } } @@ -129,7 +125,7 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, void LinkedModelGroupView::removeControl(const QString& key) { auto itr = m_widgets.find(key.toStdString()); - if(itr != m_widgets.end()) + if (itr != m_widgets.end()) { QLayoutItem* item = m_layout->itemByString(key); Q_ASSERT(!!item); @@ -157,7 +153,7 @@ void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) { LinkedModelGroupView* groupView = getGroupView(); LinkedModelGroup* group0 = groups->getGroup(0); - if(group0 && groupView) + if (group0 && groupView) { groupView->modelChanged(group0); } From e6319375fb3af87298938b72233e0da1599dd153 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 10 Dec 2019 16:51:44 +0100 Subject: [PATCH 058/120] Fix broken doc/wiki submodule --- doc/wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/wiki b/doc/wiki index 42193f98f37..19179c6f6af 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 42193f98f37d6b69f47edbdfd50a20090193e70a +Subproject commit 19179c6f6afb422cf8376ed3b4a498a6396fc12f From 36f1e6a2c28f137fb76033d2d1c6309adbeee1b0 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 11 Dec 2019 08:58:31 +0100 Subject: [PATCH 059/120] Do no set minimumSize for the view When setting miminumSize, this somehow does not work well with `PluginView`, leading to many knobs not being visible. With this commit, the `PluginView` is resized well to fit its contents. --- src/gui/widgets/LinkedModelGroupViews.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 46f6bc4e332..22fb60e47a8 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -42,9 +42,6 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, m_colNum(colNum), m_layout(new LinkedModelGroupLayout(this)) { - // make viewable: if there are no knobs, the user should at least see - // a rectangle to drop controls in - setMinimumSize(QSize(50, 50)); } From 0ae2ac0d7c4a7bdeb30606940880fa2e8be4b2ed Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 21 Dec 2019 16:37:16 +0100 Subject: [PATCH 060/120] Disable VST in this release, it's buggy --- plugins/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 389ab281ef8..f166ee0a5ed 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -78,9 +78,10 @@ IF("${PLUGIN_LIST}" STREQUAL "") stereo_enhancer stereo_matrix stk - vst_base - vestige - VstEffect + # all VST are buggy in this PR, remove them temporarily + # vst_base + # vestige + # VstEffect watsyn waveshaper vibed From 23b6b1d443e4baa57467499b6b6b124355214da0 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 21 Dec 2019 22:48:30 +0100 Subject: [PATCH 061/120] Rename LinkedModelGroupLayout to ControlLayout --- ...nkedModelGroupLayout.h => ControlLayout.h} | 18 ++++---- include/LinkedModelGroupViews.h | 2 +- src/gui/CMakeLists.txt | 2 +- ...ModelGroupLayout.cpp => ControlLayout.cpp} | 46 +++++++++---------- src/gui/widgets/LinkedModelGroupViews.cpp | 4 +- 5 files changed, 36 insertions(+), 36 deletions(-) rename include/{LinkedModelGroupLayout.h => ControlLayout.h} (93%) rename src/gui/widgets/{LinkedModelGroupLayout.cpp => ControlLayout.cpp} (85%) diff --git a/include/LinkedModelGroupLayout.h b/include/ControlLayout.h similarity index 93% rename from include/LinkedModelGroupLayout.h rename to include/ControlLayout.h index 3e0989c930a..39a64b5cc54 100644 --- a/include/LinkedModelGroupLayout.h +++ b/include/ControlLayout.h @@ -1,5 +1,5 @@ /* - * LinkedModelGroupLayout.h - layout for LinkedModelGroup class + * ControlLayout.h - layout for controls * * Copyright (c) 2019-2019 Johannes Lorenz * @@ -70,8 +70,8 @@ ** ****************************************************************************/ -#ifndef LINKEDMODELGROUPLAYOUT_H -#define LINKEDMODELGROUPLAYOUT_H +#ifndef CONTROLLAYOUT_H +#define CONTROLLAYOUT_H #include #include @@ -81,19 +81,21 @@ class QRect; class QString; /** - Layout for models + Layout for controls (models) + + Originally token from Qt's FlowLayout example. Modified. Features a search bar, as well as looking up widgets with string keys Keys have to be provided in the widgets' objectNames */ -class LinkedModelGroupLayout : public QLayout +class ControlLayout : public QLayout { Q_OBJECT public: - explicit LinkedModelGroupLayout(QWidget *parent, + explicit ControlLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); - ~LinkedModelGroupLayout() override; + ~ControlLayout() override; void addItem(QLayoutItem *item) override; int horizontalSpacing() const; @@ -126,4 +128,4 @@ private slots: class QLineEdit* m_searchBar; }; -#endif // LINKEDMODELGROUPLAYOUT_H +#endif // CONTROLLAYOUT_H diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 94f416333fe..0787aa177f3 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -72,7 +72,7 @@ class LinkedModelGroupView : public QWidget //! column number in surrounding grid in LinkedModelGroupsView std::size_t m_colNum; - class LinkedModelGroupLayout* m_layout; + class ControlLayout* m_layout; std::map> m_widgets; }; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 589a01a1abd..ae03bc8d3ea 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -68,7 +68,7 @@ SET(LMMS_SRCS gui/widgets/LcdSpinBox.cpp gui/widgets/LcdWidget.cpp gui/widgets/LedCheckbox.cpp - gui/widgets/LinkedModelGroupLayout.cpp + gui/widgets/ControlLayout.cpp gui/widgets/LinkedModelGroupViews.cpp gui/widgets/MeterDialog.cpp gui/widgets/MidiPortMenu.cpp diff --git a/src/gui/widgets/LinkedModelGroupLayout.cpp b/src/gui/widgets/ControlLayout.cpp similarity index 85% rename from src/gui/widgets/LinkedModelGroupLayout.cpp rename to src/gui/widgets/ControlLayout.cpp index fe935c0123e..0f698fe781c 100644 --- a/src/gui/widgets/LinkedModelGroupLayout.cpp +++ b/src/gui/widgets/ControlLayout.cpp @@ -1,5 +1,5 @@ /* - * LinkedModelGroups.cpp - implementation for LinkedModelGroups.h + * ControlLayout.cpp - implementation for ControlLayout.h * * Copyright (c) 2019-2019 Johannes Lorenz * @@ -71,7 +71,7 @@ ****************************************************************************/ -#include "LinkedModelGroupLayout.h" +#include "ControlLayout.h" #include #include @@ -79,11 +79,9 @@ #include #include -// TODO: rename: ModelGroupLayout +constexpr const int ControlLayout::m_minWidth; -constexpr const int LinkedModelGroupLayout::m_minWidth; - -LinkedModelGroupLayout::LinkedModelGroupLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) +ControlLayout::ControlLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing), m_searchBar(new QLineEdit(parent)) { @@ -95,19 +93,19 @@ LinkedModelGroupLayout::LinkedModelGroupLayout(QWidget *parent, int margin, int m_searchBar->setHidden(true); // nothing to filter yet } -LinkedModelGroupLayout::~LinkedModelGroupLayout() +ControlLayout::~ControlLayout() { QLayoutItem *item; while ((item = takeAt(0))) { delete item; } } -void LinkedModelGroupLayout::onTextChanged(const QString&) +void ControlLayout::onTextChanged(const QString&) { invalidate(); update(); } -void LinkedModelGroupLayout::addItem(QLayoutItem *item) +void ControlLayout::addItem(QLayoutItem *item) { QWidget* widget = item->widget(); const QString str = widget ? widget->objectName() : QString("unnamed"); @@ -115,7 +113,7 @@ void LinkedModelGroupLayout::addItem(QLayoutItem *item) invalidate(); } -int LinkedModelGroupLayout::horizontalSpacing() const +int ControlLayout::horizontalSpacing() const { if (m_hSpace >= 0) { return m_hSpace; } else @@ -124,7 +122,7 @@ int LinkedModelGroupLayout::horizontalSpacing() const } } -int LinkedModelGroupLayout::verticalSpacing() const +int ControlLayout::verticalSpacing() const { if (m_vSpace >= 0) { return m_vSpace; } else @@ -133,13 +131,13 @@ int LinkedModelGroupLayout::verticalSpacing() const } } -int LinkedModelGroupLayout::count() const +int ControlLayout::count() const { return m_itemMap.size() - 1; } QMap::const_iterator -LinkedModelGroupLayout::pairAt(int index) const +ControlLayout::pairAt(int index) const { if (index < 0) return m_itemMap.cend(); @@ -150,53 +148,53 @@ LinkedModelGroupLayout::pairAt(int index) const } // linear time :-( -QLayoutItem *LinkedModelGroupLayout::itemAt(int index) const +QLayoutItem *ControlLayout::itemAt(int index) const { auto itr = pairAt(index); return itr == m_itemMap.end() ? nullptr : itr.value(); } -QLayoutItem *LinkedModelGroupLayout::itemByString(const QString &key) const +QLayoutItem *ControlLayout::itemByString(const QString &key) const { auto itr = m_itemMap.find(key); return (itr == m_itemMap.end()) ? nullptr : *itr; } // linear time :-( -QLayoutItem *LinkedModelGroupLayout::takeAt(int index) +QLayoutItem *ControlLayout::takeAt(int index) { auto itr = pairAt(index); return (itr == m_itemMap.end()) ? nullptr : m_itemMap.take(itr.key()); } -Qt::Orientations LinkedModelGroupLayout::expandingDirections() const +Qt::Orientations ControlLayout::expandingDirections() const { return Qt::Orientations(); } -bool LinkedModelGroupLayout::hasHeightForWidth() const +bool ControlLayout::hasHeightForWidth() const { return true; } -int LinkedModelGroupLayout::heightForWidth(int width) const +int ControlLayout::heightForWidth(int width) const { int height = doLayout(QRect(0, 0, width, 0), true); return height; } -void LinkedModelGroupLayout::setGeometry(const QRect &rect) +void ControlLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); doLayout(rect, false); } -QSize LinkedModelGroupLayout::sizeHint() const +QSize ControlLayout::sizeHint() const { return minimumSize(); } -QSize LinkedModelGroupLayout::minimumSize() const +QSize ControlLayout::minimumSize() const { // original formula from Qt's FlowLayout example: // get maximum height and width for all children. @@ -215,7 +213,7 @@ QSize LinkedModelGroupLayout::minimumSize() const return size; } -int LinkedModelGroupLayout::doLayout(const QRect &rect, bool testOnly) const +int ControlLayout::doLayout(const QRect &rect, bool testOnly) const { int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); @@ -288,7 +286,7 @@ int LinkedModelGroupLayout::doLayout(const QRect &rect, bool testOnly) const return y + lineHeight - rect.y() + bottom; } -int LinkedModelGroupLayout::smartSpacing(QStyle::PixelMetric pm) const +int ControlLayout::smartSpacing(QStyle::PixelMetric pm) const { QObject *parent = this->parent(); if (!parent) { return -1; } diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index 22fb60e47a8..c53d0d73ece 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -26,7 +26,7 @@ #include #include "Controls.h" -#include "LinkedModelGroupLayout.h" +#include "ControlLayout.h" #include "LinkedModelGroups.h" @@ -40,7 +40,7 @@ LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, QWidget(parent), m_model(model), m_colNum(colNum), - m_layout(new LinkedModelGroupLayout(this)) + m_layout(new ControlLayout(this)) { } From 14c72b0b0f3ab2767cedf91d9cedb17026c299cf Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 31 Dec 2019 00:10:16 +0100 Subject: [PATCH 062/120] Implement LinkedModelGroup::containsModel --- include/LinkedModelGroups.h | 1 + src/core/LinkedModelGroups.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 87013322464..735b9825998 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -94,6 +94,7 @@ class LinkedModelGroup : public Model } std::size_t modelNum() const { return m_models.size(); } + bool containsModel(const QString& name) const; // this is bad style (redirecting into the sub-class), but this class // will be married with the sub-classes (Lv2Proc, SpaProc) anyways, diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 87420b83799..062455c023e 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -91,6 +91,14 @@ void LinkedModelGroup::clearModels() +bool LinkedModelGroup::containsModel(const QString &name) const +{ + return m_models.find(name.toStdString()) != m_models.end(); +} + + + + /* LinkedModelGroups */ From 66413803c43c75aed98f5b0efdb71c60c7be7296 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 2 Jan 2020 21:02:26 +0100 Subject: [PATCH 063/120] Style fixes, thanks to @Veratil --- src/gui/widgets/ControlLayout.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/gui/widgets/ControlLayout.cpp b/src/gui/widgets/ControlLayout.cpp index 0f698fe781c..4863a5d6848 100644 --- a/src/gui/widgets/ControlLayout.cpp +++ b/src/gui/widgets/ControlLayout.cpp @@ -139,8 +139,7 @@ int ControlLayout::count() const QMap::const_iterator ControlLayout::pairAt(int index) const { - if (index < 0) - return m_itemMap.cend(); + if (index < 0) { return m_itemMap.cend(); } QMap::const_iterator itr = m_itemMap.cbegin(); ++itr; // skip search bar while (index-->0 && itr != m_itemMap.cend()) { ++itr; } @@ -151,7 +150,7 @@ ControlLayout::pairAt(int index) const QLayoutItem *ControlLayout::itemAt(int index) const { auto itr = pairAt(index); - return itr == m_itemMap.end() ? nullptr : itr.value(); + return (itr == m_itemMap.end()) ? nullptr : itr.value(); } QLayoutItem *ControlLayout::itemByString(const QString &key) const @@ -231,12 +230,11 @@ int ControlLayout::doLayout(const QRect &rect, bool testOnly) const itr.next(); QLayoutItem* item = itr.value(); QWidget *wid = item->widget(); - - if (first || // do not filter search bar - filterText.isEmpty() || // no filter - pass all - itr.key().contains(filterText, Qt::CaseInsensitive)) + if (wid) { - if (wid) + if ( first || // do not filter search bar + filterText.isEmpty() || // no filter - pass all + itr.key().contains(filterText, Qt::CaseInsensitive)) { if (first) { @@ -277,10 +275,10 @@ int ControlLayout::doLayout(const QRect &rect, bool testOnly) const lineHeight = qMax(lineHeight, item->sizeHint().height()); first = false; } - } - else - { - if (wid) { wid->hide(); } + else + { + wid->hide(); + } } } return y + lineHeight - rect.y() + bottom; From cf238259c77c125ee40b9836da55a986f8f433f7 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 2 Jan 2020 21:54:26 +0100 Subject: [PATCH 064/120] LinkedModelGroup::ModelInfo: Delete default CTOR --- include/LinkedModelGroups.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 735b9825998..7dad5d0034f 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -69,7 +69,7 @@ class LinkedModelGroup : public Model { QString m_name; class AutomatableModel* m_model; - ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? + ModelInfo() = delete; ModelInfo(const QString& name, AutomatableModel* model) : m_name(name), m_model(model) {} }; From 0ff18776bb883d8227213929c98b7b31504ce728 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 2 Jan 2020 21:54:39 +0100 Subject: [PATCH 065/120] Try to fix CI error --- src/gui/widgets/LinkedModelGroupViews.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp index c53d0d73ece..21d40efcc1e 100644 --- a/src/gui/widgets/LinkedModelGroupViews.cpp +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -109,7 +109,8 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, box->setObjectName(QString::fromStdString(display)); m_layout->addWidget(box); - m_widgets.emplace(id, ctrl); + // take ownership of control and add it + m_widgets.emplace(id, std::unique_ptr(ctrl)); ++wdgNum; } From 0a97c6b1d6ae03a898724e8d55b1002bc48c7cb8 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 2 Jan 2020 22:14:21 +0100 Subject: [PATCH 066/120] Fix broken wiki submodule --- doc/wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/wiki b/doc/wiki index 42193f98f37..19179c6f6af 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 42193f98f37d6b69f47edbdfd50a20090193e70a +Subproject commit 19179c6f6afb422cf8376ed3b4a498a6396fc12f From 87c4a03297e494725818da82e3e951b71892cbbc Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 27 Jan 2020 21:18:15 +0100 Subject: [PATCH 067/120] Delete ModelInfo default CTOR --- include/LinkedModelGroups.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 7dad5d0034f..735b9825998 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -69,7 +69,7 @@ class LinkedModelGroup : public Model { QString m_name; class AutomatableModel* m_model; - ModelInfo() = delete; + ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? ModelInfo(const QString& name, AutomatableModel* model) : m_name(name), m_model(model) {} }; From c6a28ed89f88d14ef74c8594b9d03d844d36d36f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 27 Jan 2020 21:19:24 +0100 Subject: [PATCH 068/120] LinkedModelGroup: Implement eraseModel() and getModel() --- include/LinkedModelGroups.h | 9 +++++++++ src/core/LinkedModelGroups.cpp | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index 735b9825998..e7323bbec21 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -108,8 +108,17 @@ class LinkedModelGroup : public Model void loadValues(const class QDomElement& that); protected: + AutomatableModel* getModel(const std::string& s) + { + auto itr = m_models.find(s); + return (itr == m_models.end()) ? nullptr : itr->second.m_model; + } + //! Register a further model void addModel(class AutomatableModel* model, const QString& name); + //! Unregister a model, return true if a model was erased + bool eraseModel(const QString& name); + //! Remove all models void clearModels(); diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 062455c023e..595c3eeee22 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -83,6 +83,14 @@ void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) +bool LinkedModelGroup::eraseModel(const QString& name) +{ + return m_models.erase(name.toStdString()) > 0; +} + + + + void LinkedModelGroup::clearModels() { m_models.clear(); From eeb1ef20bf1c84e237ec5d8c2dd71dee44231787 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 9 Feb 2020 20:05:21 +0100 Subject: [PATCH 069/120] Add helpful comments --- include/LinkedModelGroupViews.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h index 0787aa177f3..79fab76afd5 100644 --- a/include/LinkedModelGroupViews.h +++ b/include/LinkedModelGroupViews.h @@ -41,6 +41,10 @@ /** View for a representative processor + Features: + * Remove button for removable models + * Simple handling of adding, removing and model changing + @note Neither this class, nor any inheriting classes, shall inherit ModelView. The "view" in the name is just for consistency with LinkedModelGroupsView. From c9f03bacb9ceb7c5c95b986aaf30f798a1e062ea Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 9 Feb 2020 20:08:02 +0100 Subject: [PATCH 070/120] Move removeControl from subclasses into this class This is just refactoring of removeControl, which leads to adding signals for added and removed signals. See #4662 for an example. No functional changes. --- include/LinkedModelGroups.h | 17 +++++++++++------ src/core/LinkedModelGroups.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h index e7323bbec21..355290d9aee 100644 --- a/include/LinkedModelGroups.h +++ b/include/LinkedModelGroups.h @@ -50,6 +50,7 @@ */ class LinkedModelGroup : public Model { + Q_OBJECT public: /* Initialization @@ -95,11 +96,7 @@ class LinkedModelGroup : public Model std::size_t modelNum() const { return m_models.size(); } bool containsModel(const QString& name) const; - - // this is bad style (redirecting into the sub-class), but this class - // will be married with the sub-classes (Lv2Proc, SpaProc) anyways, - // so let's do the dirty trick for now... - virtual void removeControl(AutomatableModel *) {} + void removeControl(AutomatableModel *); /* Load/Save @@ -107,7 +104,15 @@ class LinkedModelGroup : public Model void saveValues(class QDomDocument& doc, class QDomElement& that); void loadValues(const class QDomElement& that); -protected: +signals: + // NOTE: when separating core from UI, this will need to be removed + // (who would kno if the client is Qt, i.e. it may not have slots at all) + // In this case you'd e.g. send the UI something like + // "/added " + void modelAdded(AutomatableModel* added); + void modelRemoved(AutomatableModel* removed); + +public: AutomatableModel* getModel(const std::string& s) { auto itr = m_models.find(s); diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 595c3eeee22..c9bbc475a8f 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -78,6 +78,31 @@ void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) { model->setObjectName(name); m_models.emplace(std::string(name.toUtf8().data()), ModelInfo(name, model)); + connect(model, &AutomatableModel::destroyed, + this, [this, model](jo_id_t){ + if(containsModel(model->objectName())) + { + emit modelRemoved(model); + eraseModel(model->objectName()); + } + }, + Qt::DirectConnection); + + // View needs to create another child view, e.g. a new knob: + emit modelAdded(model); + emit dataChanged(); +} + + + + +void LinkedModelGroup::removeControl(AutomatableModel* mdl) +{ + if(containsModel(mdl->objectName())) + { + emit modelRemoved(mdl); + eraseModel(mdl->objectName()); + } } From 210d7044af5b96095d514f6e6bd1c80afe8283f1 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 12 Feb 2020 06:29:08 +0100 Subject: [PATCH 071/120] Detect search bar by name, not by position --- include/ControlLayout.h | 2 ++ src/gui/widgets/ControlLayout.cpp | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/ControlLayout.h b/include/ControlLayout.h index 39a64b5cc54..625b3f4681d 100644 --- a/include/ControlLayout.h +++ b/include/ControlLayout.h @@ -126,6 +126,8 @@ private slots: // 400 looks good and is ~4 knobs in a row constexpr const static int m_minWidth = 400; class QLineEdit* m_searchBar; + //! name of search bar, must be ASCII sorted before any alpha numerics + static constexpr const char* s_searchBarName = "!!searchBar!!"; }; #endif // CONTROLLAYOUT_H diff --git a/src/gui/widgets/ControlLayout.cpp b/src/gui/widgets/ControlLayout.cpp index 4863a5d6848..afd4e68e42e 100644 --- a/src/gui/widgets/ControlLayout.cpp +++ b/src/gui/widgets/ControlLayout.cpp @@ -87,6 +87,7 @@ ControlLayout::ControlLayout(QWidget *parent, int margin, int hSpacing, int vSpa { setContentsMargins(margin, margin, margin, margin); m_searchBar->setPlaceholderText("filter"); + m_searchBar->setObjectName(s_searchBarName); connect(m_searchBar, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged(const QString& ))); addWidget(m_searchBar); @@ -140,9 +141,17 @@ QMap::const_iterator ControlLayout::pairAt(int index) const { if (index < 0) { return m_itemMap.cend(); } + + auto skip = [&](QLayoutItem* item) -> bool + { + return item->widget()->objectName() == s_searchBarName; + }; + QMap::const_iterator itr = m_itemMap.cbegin(); - ++itr; // skip search bar - while (index-->0 && itr != m_itemMap.cend()) { ++itr; } + for (; itr != m_itemMap.cend() && (index > 0 || skip(itr.value())); ++itr) + { + if(!skip(itr.value())) { index--; } + } return itr; } From 55a5d739acd0fa871faad145b1bb91e7467881ab Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 30 Mar 2020 19:20:12 +0200 Subject: [PATCH 072/120] Add plugin load summary --- src/core/lv2/Lv2Manager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index f84a0e27441..1bb1099a924 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "ConfigManager.h" #include "Plugin.h" @@ -86,6 +87,9 @@ const LilvPlugin *Lv2Manager::getPlugin(const QString uri) void Lv2Manager::initPlugins() { const LilvPlugins* plugins = lilv_world_get_all_plugins(m_world); + std::size_t pluginCount = 0, pluginsLoaded = 0; + QElapsedTimer timer; + timer.start(); LILV_FOREACH(plugins, itr, plugins) { @@ -97,7 +101,13 @@ void Lv2Manager::initPlugins() m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] = std::move(info); + pluginsLoaded += issues.empty(); + ++pluginCount; } + + qDebug() << "Lv2 plugin SUMMARY:" + << pluginsLoaded << "of" << pluginCount << " loaded in" + << timer.elapsed() << "msecs."; } From c0a3e13ea91feb9243d0aec0c8fa16877d4838ca Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 17 Apr 2020 19:58:03 +0200 Subject: [PATCH 073/120] Lv2ControlBase: Don't expose vector storage --- include/Lv2ControlBase.h | 4 +++- src/gui/Lv2ViewBase.cpp | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 458414231ee..e4d3f04eebf 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -73,9 +73,11 @@ class Lv2ControlBase : public LinkedModelGroups 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; } - std::vector>& controls() { return m_procs; } protected: /* diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 06c3848ac9f..e62cd81546b 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -191,9 +191,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); } - m_procView = new Lv2ViewProc(meAsWidget, - ctrlBase->controls()[static_cast(0)].get(), - m_colNum); + m_procView = new Lv2ViewProc(meAsWidget, ctrlBase->control(0), m_colNum); grid->addWidget(m_procView, Rows::ProcRow, 0); } From 2c540bda195301016b1b930c4a0a9ac529f47c74 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 17 Apr 2020 20:00:11 +0200 Subject: [PATCH 074/120] Move Lv2Manager::m_world to other privates --- include/Lv2Manager.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 2815799cffa..c110dbc3c9f 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -73,8 +73,6 @@ //! Class to keep track of all LV2 plugins class Lv2Manager { - LilvWorld* m_world; - public: void initPlugins(); @@ -117,6 +115,7 @@ class Lv2Manager Iterator end() { return Iterator(m_lv2InfoMap.end()); } private: + LilvWorld* m_world; std::map m_lv2InfoMap; bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; From f10afccf82d08746606fc7ac004c880f70f3e104 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 11:44:02 +0200 Subject: [PATCH 075/120] Remove Lv2Manager::Lv2Info copy CTOR deletion The copy CTOR is already implicitly deleted by the move CTOR --- include/Lv2Manager.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index c110dbc3c9f..e86457a1c1a 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -91,7 +91,6 @@ class Lv2Manager //! ctor used inside Lv2Manager Lv2Info(const LilvPlugin* plug, Plugin::PluginTypes type, bool valid) : m_plugin(plug), m_type(type), m_valid(valid) {} - Lv2Info(const Lv2Info &) = delete; Lv2Info(Lv2Info&& other) = default; Lv2Info& operator=(Lv2Info&& other) = default; From 38174ca38373763ebb2d6518fe8d0fd6c71748ca Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 18 Apr 2020 20:26:33 +0200 Subject: [PATCH 076/120] Introduce type Lv2Manager::Lv2InfoMap --- include/Lv2Manager.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index e86457a1c1a..1ff5667c527 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -109,13 +109,14 @@ class Lv2Manager //! Return descriptor with URI @p uri or nullptr if none exists const LilvPlugin *getPlugin(const QString uri); - using Iterator = std::map::iterator; + using Lv2InfoMap = std::map; + using Iterator = Lv2InfoMap::iterator; Iterator begin() { return Iterator(m_lv2InfoMap.begin()); } Iterator end() { return Iterator(m_lv2InfoMap.end()); } private: LilvWorld* m_world; - std::map m_lv2InfoMap; + Lv2InfoMap m_lv2InfoMap; bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); }; From a1600008371d961756a901aaabc5e1fe9a1725d0 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 18 Apr 2020 20:29:36 +0200 Subject: [PATCH 077/120] Lv2Manager: Remove unused Iterator casts --- include/Lv2Manager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 1ff5667c527..65e52b2ae90 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -111,8 +111,8 @@ class Lv2Manager using Lv2InfoMap = std::map; using Iterator = Lv2InfoMap::iterator; - Iterator begin() { return Iterator(m_lv2InfoMap.begin()); } - Iterator end() { return Iterator(m_lv2InfoMap.end()); } + Iterator begin() { return m_lv2InfoMap.begin(); } + Iterator end() { return m_lv2InfoMap.end(); } private: LilvWorld* m_world; From f0fc456df29fa65d576203992d7f51381944f933 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 18 Apr 2020 21:44:42 +0200 Subject: [PATCH 078/120] Replace IS_PORT_TYPE with CRTP --- include/Lv2Ports.h | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 06a99a8c67b..86da9a330b8 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -125,14 +125,15 @@ struct PortBase : public Meta virtual ~PortBase(); }; -#define IS_PORT_TYPE \ -void accept(Visitor& v) override { v.visit(*this); } \ -void accept(ConstVisitor& v) const override { v.visit(*this); } - -struct ControlPortBase : public PortBase +template +struct VisitablePort : public Base { - IS_PORT_TYPE + void accept(Visitor& v) override { v.visit(static_cast(*this)); } + void accept(ConstVisitor& v) const override { v.visit(static_cast(*this)); } +}; +struct ControlPortBase : public VisitablePort +{ //! LMMS models //! Always up-to-date, except during runs std::unique_ptr m_connectedModel; @@ -143,30 +144,32 @@ struct ControlPortBase : public PortBase std::vector m_scalePointMap; }; -struct Control : public ControlPortBase +struct Control : public VisitablePort { - IS_PORT_TYPE - //! Data location which Lv2 plugins see //! Model values are being copied here every run //! Between runs, this data is not up-to-date float m_val; + + // overwrite accept() from ControlPortBase + void accept(Visitor& v) override { v.visit(*this); } + void accept(ConstVisitor& v) const override { v.visit(*this); } }; -struct Cv : public ControlPortBase +struct Cv : public VisitablePort { - IS_PORT_TYPE - //! Data location which Lv2 plugins see //! Model values are being copied here every run //! Between runs, this data is not up-to-date std::vector m_buffer; + + // overwrite accept() from ControlPortBase + void accept(Visitor& v) override { v.visit(*this); } + void accept(ConstVisitor& v) const override { v.visit(*this); } }; -struct Audio : public PortBase +struct Audio : public VisitablePort { - IS_PORT_TYPE - Audio(std::size_t bufferSize, bool isSidechain); //! Copy buffer passed by LMMS into our ports @@ -190,11 +193,9 @@ struct Audio : public PortBase friend struct ::ConnectPortVisitor; }; -struct Unknown : public PortBase +struct Unknown : public VisitablePort { - IS_PORT_TYPE }; -#undef IS_PORT_TYPE /* port casts From 289051ce38ed052332535a2dc1a258924e1cb6d4 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 18 Apr 2020 22:13:12 +0200 Subject: [PATCH 079/120] ~Lv2Proc: use override instead of virtual --- include/Lv2Proc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 248f7874901..58e8ed6fb5c 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -63,7 +63,7 @@ class Lv2Proc : public LinkedModelGroup ctor/dtor */ Lv2Proc(const LilvPlugin* plugin, Model *parent); - virtual ~Lv2Proc(); + ~Lv2Proc() override; //! Must be checked after ctor or reload bool isValid() const { return m_valid; } From e8e89cf8b7d98c2f349a3110031a537e6e962fca Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 00:10:26 +0200 Subject: [PATCH 080/120] Lv2Proc: Improve port access * Replace getPorts by foreach_port to hide internals * Lv2ViewProc: Make SetupWidget a ConstVisitor --- include/Lv2Proc.h | 18 ++++++++++++++++-- src/gui/Lv2ViewBase.cpp | 35 ++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 58e8ed6fb5c..b04a7bdc984 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -82,8 +82,22 @@ class Lv2Proc : public LinkedModelGroup const StereoPortRef& inPorts() const { return m_inPorts; } StereoPortRef& outPorts() { return m_outPorts; } const StereoPortRef& outPorts() const { return m_outPorts; } - std::vector>& getPorts() { return m_ports; } - const std::vector>& getPorts() const { return m_ports; } + template + void foreach_port(const Functor& ftor) + { + for (std::unique_ptr& port : m_ports) + { + ftor(port.get()); + } + } + template + void foreach_port(const Functor& ftor) const + { + for (const std::unique_ptr& port : m_ports) + { + ftor(port.get()); + } + } //! Debug function to print ports to stdout void dumpPorts(); diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index e62cd81546b..92e953fb811 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -52,13 +52,13 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : LinkedModelGroupView (parent, ctrlBase, colNum) { - class SetupWidget : public Lv2Ports::Visitor + class SetupWidget : public Lv2Ports::ConstVisitor { public: QWidget* m_par; // input const AutoLilvNode* m_commentUri; // input Control* m_control = nullptr; // output - void visit(Lv2Ports::Control& port) override + void visit(const Lv2Ports::Control& port) override { if (port.m_flow == Lv2Ports::Flow::Input) { @@ -96,22 +96,23 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : }; AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); - for (std::unique_ptr& port : ctrlBase->getPorts()) - { - SetupWidget setup; - setup.m_par = this; - setup.m_commentUri = &commentUri; - port->accept(setup); - - if (setup.m_control) + ctrlBase->foreach_port( + [this, &commentUri](const Lv2Ports::PortBase* port) { - addControl(setup.m_control, - lilv_node_as_string(lilv_port_get_symbol( - port->m_plugin, port->m_port)), - port->name().toUtf8().data(), - false); - } - } + SetupWidget setup; + setup.m_par = this; + setup.m_commentUri = &commentUri; + port->accept(setup); + + if (setup.m_control) + { + addControl(setup.m_control, + lilv_node_as_string(lilv_port_get_symbol( + port->m_plugin, port->m_port)), + port->name().toUtf8().data(), + false); + } + }); } From a54481098a4be13a6884a6d276a98abf0df09785 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 19:43:52 +0200 Subject: [PATCH 081/120] Lv2Effect::processAudioBuffer: Remove useless alias --- plugins/Lv2Effect/Lv2Effect.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index e19b37a1ed3..1257325e940 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -72,16 +72,14 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false; } - Lv2FxControls& ctrl = m_controls; - - ctrl.copyBuffersFromLmms(buf, frames); + m_controls.copyBuffersFromLmms(buf, frames); m_controls.copyModelsFromLmms(); // m_pluginMutex.lock(); - ctrl.run(frames); + m_controls.run(frames); // m_pluginMutex.unlock(); - ctrl.copyBuffersToLmms(buf, frames); + m_controls.copyBuffersToLmms(buf, frames); double outSum = .0; for(fpp_t f = 0; f < frames; ++f) From e9c46ad088f309c5b63fa31fb502e5d8d31509b7 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 19:53:19 +0200 Subject: [PATCH 082/120] Lv2FxControls must not be a friend of Lv2Effect --- plugins/Lv2Effect/Lv2Effect.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index adf9157ae95..5d2d3e25d4a 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -49,8 +49,6 @@ class Lv2Effect : public Effect private: Lv2FxControls m_controls; - - friend class Lv2FxControls; }; #endif // LMMS_HAVE_LV2 From 960bf7c274c5ae06425b31654b9c3c3c2be3dd0d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:12:50 +0200 Subject: [PATCH 083/120] Update all Lv2 Copyright headers --- include/Lv2Basics.h | 2 +- include/Lv2ControlBase.h | 2 +- include/Lv2Manager.h | 2 +- include/Lv2Ports.h | 4 ++-- include/Lv2Proc.h | 2 +- include/Lv2SubPluginFeatures.h | 2 +- include/Lv2ViewBase.h | 2 +- plugins/Lv2Effect/Lv2Effect.cpp | 4 ++-- plugins/Lv2Effect/Lv2Effect.h | 2 +- plugins/Lv2Effect/Lv2FxControlDialog.cpp | 4 ++-- plugins/Lv2Effect/Lv2FxControlDialog.h | 4 ++-- plugins/Lv2Effect/Lv2FxControls.cpp | 4 ++-- plugins/Lv2Effect/Lv2FxControls.h | 4 ++-- plugins/Lv2Instrument/Lv2Instrument.cpp | 4 ++-- plugins/Lv2Instrument/Lv2Instrument.h | 2 +- src/core/lv2/Lv2Basics.cpp | 2 +- src/core/lv2/Lv2ControlBase.cpp | 2 +- src/core/lv2/Lv2Manager.cpp | 2 +- src/core/lv2/Lv2Ports.cpp | 4 ++-- src/core/lv2/Lv2Proc.cpp | 2 +- src/core/lv2/Lv2SubPluginFeatures.cpp | 3 +-- src/gui/Lv2ViewBase.cpp | 2 +- 22 files changed, 30 insertions(+), 31 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 12ff50e6f00..08e993b6343 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -1,7 +1,7 @@ /* * Lv2Basics.h - basic Lv2 utils * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index e4d3f04eebf..cad94fe474b 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -1,7 +1,7 @@ /* * Lv2ControlBase.h - Lv2 control base class * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 65e52b2ae90..02eb763d58a 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -1,7 +1,7 @@ /* * Lv2Manager.h - Implementation of Lv2Manager class * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 86da9a330b8..17710a59c39 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -1,7 +1,7 @@ /* - * Lv2Ports.h - Lv2 port definitions + * Lv2Ports.h - Lv2 port classes definitions * - * Copyright (c) 2019-2019 Johannes Lorenz + * Copyright (c) 2019-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index b04a7bdc984..48d5fe6da5b 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -1,7 +1,7 @@ /* * Lv2Proc.h - Lv2 processor class * - * Copyright (c) 2019-2019 Johannes Lorenz + * Copyright (c) 2019-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h index edf42c45499..c4f14dafb89 100644 --- a/include/Lv2SubPluginFeatures.h +++ b/include/Lv2SubPluginFeatures.h @@ -3,7 +3,7 @@ * Plugin::Descriptor::SubPluginFeatures for * hosting LV2 plugins * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index a965c23ca58..980e1f7efcf 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -1,7 +1,7 @@ /* * Lv2ViewBase.h - base class for Lv2 plugin views * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 1257325e940..1e6f8730a01 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -1,7 +1,7 @@ /* * Lv2Effect.cpp - implementation of LV2 effect * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = "LV2", QT_TRANSLATE_NOOP("pluginBrowser", "plugin for using arbitrary LV2-effects inside LMMS."), - "Johannes Lorenz ", + "Johannes Lorenz ", 0x0100, Plugin::Effect, new PluginPixmapLoader("logo"), diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index 5d2d3e25d4a..edff320f816 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -1,7 +1,7 @@ /* * Lv2Effect.h - implementation of LV2 effect * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index 9f00031174f..51c83f68e26 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -1,7 +1,7 @@ /* - * Lv2ControlDialog.cpp - control dialog for amplifier effect + * Lv2FxControlDialog.cpp - Lv2FxControlDialog implementation * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.h b/plugins/Lv2Effect/Lv2FxControlDialog.h index 9edd7441454..74a9273dfa3 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.h +++ b/plugins/Lv2Effect/Lv2FxControlDialog.h @@ -1,7 +1,7 @@ /* - * Lv2ControlDialog.h - control dialog for amplifier effect + * Lv2FxControlDialog.h - Lv2FxControlDialog implementation * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/plugins/Lv2Effect/Lv2FxControls.cpp b/plugins/Lv2Effect/Lv2FxControls.cpp index aa0f0df2df9..07256ec09df 100644 --- a/plugins/Lv2Effect/Lv2FxControls.cpp +++ b/plugins/Lv2Effect/Lv2FxControls.cpp @@ -1,7 +1,7 @@ /* - * Lv2Controls.cpp - controls for amplifier effect + * Lv2FxControls.cpp - Lv2FxControls implementation * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/plugins/Lv2Effect/Lv2FxControls.h b/plugins/Lv2Effect/Lv2FxControls.h index e5b4a2a7f58..af4706646e5 100644 --- a/plugins/Lv2Effect/Lv2FxControls.h +++ b/plugins/Lv2Effect/Lv2FxControls.h @@ -1,7 +1,7 @@ /* - * Lv2Controls.h - controls for bassboosterx -effect + * Lv2FxControls.h - Lv2FxControls implementation * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 87e590ddb6c..172bd2986c0 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -1,7 +1,7 @@ /* * Lv2Instrument.cpp - implementation of LV2 instrument * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -46,7 +46,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = "LV2", QT_TRANSLATE_NOOP("pluginBrowser", "plugin for using arbitrary LV2 instruments inside LMMS."), - "Johannes Lorenz ", + "Johannes Lorenz ", 0x0100, Plugin::Instrument, new PluginPixmapLoader("logo"), diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 1c3b096d3a7..c0d32ee64bc 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -1,7 +1,7 @@ /* * Lv2Instrument.h - implementation of LV2 instrument * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/core/lv2/Lv2Basics.cpp b/src/core/lv2/Lv2Basics.cpp index 6b03ffa2371..64779823572 100644 --- a/src/core/lv2/Lv2Basics.cpp +++ b/src/core/lv2/Lv2Basics.cpp @@ -1,7 +1,7 @@ /* * Lv2Basics.cpp - basic Lv2 functions * - * Copyright (c) 2019-2019 Johannes Lorenz + * Copyright (c) 2019-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 84e396b5c5a..c148a1e3d73 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -1,7 +1,7 @@ /* * Lv2ControlBase.cpp - Lv2 control base class * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 1bb1099a924..e1febf71558 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -1,7 +1,7 @@ /* * Lv2Manager.cpp - Implementation of Lv2Manager class * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index df5df690875..9c269bcfa62 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -1,7 +1,7 @@ /* - * Lv2Ports.cpp - Lv2 port implementation + * Lv2Ports.cpp - Lv2 port classes implementation * - * Copyright (c) 2019-2019 Johannes Lorenz + * Copyright (c) 2019-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index e901b773d1e..6f99db3ccaa 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -1,7 +1,7 @@ /* * Lv2Proc.cpp - Lv2 processor class * - * Copyright (c) 2019-2019 Johannes Lorenz + * Copyright (c) 2019-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 1014ae91688..2728b2d1bb8 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -3,8 +3,7 @@ * Plugin::Descriptor::SubPluginFeatures for * hosting LV2 plugins * - * Copyright (c) 2006-2007 Danny McRae - * Copyright (c) 2006-2014 Tobias Doerffel + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 92e953fb811..a562b4dd928 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -1,7 +1,7 @@ /* * Lv2ViewBase.cpp - base class for Lv2 plugin views * - * Copyright (c) 2018-2019 Johannes Lorenz + * Copyright (c) 2018-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * From 428e841ef28bb010ae2fef9240fd8d96c970f226 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:14:42 +0200 Subject: [PATCH 084/120] Remove unused Lv2FxControls::m_effect --- plugins/Lv2Effect/Lv2FxControls.cpp | 3 +-- plugins/Lv2Effect/Lv2FxControls.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/Lv2Effect/Lv2FxControls.cpp b/plugins/Lv2Effect/Lv2FxControls.cpp index 07256ec09df..a5733fec670 100644 --- a/plugins/Lv2Effect/Lv2FxControls.cpp +++ b/plugins/Lv2Effect/Lv2FxControls.cpp @@ -36,8 +36,7 @@ Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) : EffectControls(effect), - Lv2ControlBase(this, uri), - m_effect(effect) + Lv2ControlBase(this, uri) { if (isValid()) { diff --git a/plugins/Lv2Effect/Lv2FxControls.h b/plugins/Lv2Effect/Lv2FxControls.h index af4706646e5..ed39c73fddc 100644 --- a/plugins/Lv2Effect/Lv2FxControls.h +++ b/plugins/Lv2Effect/Lv2FxControls.h @@ -55,7 +55,6 @@ private slots: DataFile::Types settingsType() override; void setNameFromFile(const QString &name) override; - Lv2Effect *m_effect; friend class Lv2FxControlDialog; friend class Lv2Effect; }; From 1d34ba230cf3b21f9ca4baa67cc6db8077ee9de6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:20:41 +0200 Subject: [PATCH 085/120] Lv2 reload buttons: clicked instead of toggled --- plugins/Lv2Effect/Lv2FxControlDialog.cpp | 2 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index 51c83f68e26..9a77171d6df 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -37,7 +37,7 @@ Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : Lv2ViewBase(this, controls) { if (m_reloadPluginButton) { - connect(m_reloadPluginButton, &QPushButton::toggled, + connect(m_reloadPluginButton, &QPushButton::clicked, this, [this](){ lv2Controls()->reloadPlugin(); }); } if (m_toggleUIButton) { diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 172bd2986c0..e1b0704497a 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -220,7 +220,7 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : { setAutoFillBackground(true); if (m_reloadPluginButton) { - connect(m_reloadPluginButton, &QPushButton::toggled, + connect(m_reloadPluginButton, &QPushButton::clicked, this, [this](){ castModel()->reloadPlugin();} ); } if (m_toggleUIButton) { From e1f53a92a8b45bde6d3ed34765d5756b00b9e0ba Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:25:09 +0200 Subject: [PATCH 086/120] Lv2ControlBase CTOR: Use make_unique --- src/core/lv2/Lv2ControlBase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index c148a1e3d73..199ae0c05fd 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -31,6 +31,7 @@ #include "Engine.h" #include "Lv2Manager.h" #include "Lv2Proc.h" +#include "stdshims.h" @@ -53,7 +54,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo while (channelsLeft > 0) { - std::unique_ptr newOne(new Lv2Proc(m_plugin, that)); + std::unique_ptr newOne = make_unique(m_plugin, that); if (newOne->isValid()) { channelsLeft -= std::max( From c1395828dbc8f579e01ede82f2695f4bfd4638e8 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:27:40 +0200 Subject: [PATCH 087/120] Lv2ControlBase.cpp: Use auto for iterating --- src/core/lv2/Lv2ControlBase.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 199ae0c05fd..84335415b60 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -108,7 +108,7 @@ const LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) const void Lv2ControlBase::copyModelsFromLmms() { - for (std::unique_ptr& c : m_procs) { c->copyModelsFromCore(); } + for (auto& c : m_procs) { c->copyModelsFromCore(); } } @@ -116,7 +116,7 @@ void Lv2ControlBase::copyModelsFromLmms() { void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { unsigned offset = 0; - for (std::unique_ptr& c : m_procs) { + for (auto& c : m_procs) { c->copyBuffersFromCore(buf, offset, m_channelsPerProc, frames); offset += m_channelsPerProc; } @@ -127,7 +127,7 @@ void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { unsigned offset = 0; - for (const std::unique_ptr& c : m_procs) { + for (const auto& c : m_procs) { c->copyBuffersToCore(buf, offset, m_channelsPerProc, frames); offset += m_channelsPerProc; } @@ -137,7 +137,7 @@ void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { void Lv2ControlBase::run(fpp_t frames) { - for (std::unique_ptr& c : m_procs) { c->run(frames); } + for (auto& c : m_procs) { c->run(frames); } } @@ -181,7 +181,7 @@ void Lv2ControlBase::reloadPlugin() std::size_t Lv2ControlBase::controlCount() const { std::size_t res = 0; - for (const std::unique_ptr& c : m_procs) { res += c->controlCount(); } + for (const auto& c : m_procs) { res += c->controlCount(); } return res; } From b91f99d4ee34fd1df8c61fb79bfa1e284c4dca63 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:30:24 +0200 Subject: [PATCH 088/120] Lv2Manager::getPlugin: Pass QString by const ref --- include/Lv2Manager.h | 2 +- src/core/lv2/Lv2Manager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 02eb763d58a..609f684b98c 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -107,7 +107,7 @@ class Lv2Manager //! Return descriptor with URI @p uri or nullptr if none exists const LilvPlugin *getPlugin(const std::string &uri); //! Return descriptor with URI @p uri or nullptr if none exists - const LilvPlugin *getPlugin(const QString uri); + const LilvPlugin *getPlugin(const QString& uri); using Lv2InfoMap = std::map; using Iterator = Lv2InfoMap::iterator; diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index e1febf71558..173e895aafc 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -76,7 +76,7 @@ const LilvPlugin *Lv2Manager::getPlugin(const std::string &uri) -const LilvPlugin *Lv2Manager::getPlugin(const QString uri) +const LilvPlugin *Lv2Manager::getPlugin(const QString &uri) { return getPlugin(std::string(uri.toUtf8())); } From 770174b704ab12f4486df8eee423ef46fe4ced3f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:39:57 +0200 Subject: [PATCH 089/120] Lv2Manager::getPlugin: Convert QString->string directly --- src/core/lv2/Lv2Manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 173e895aafc..ff423e97198 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -78,7 +78,7 @@ const LilvPlugin *Lv2Manager::getPlugin(const std::string &uri) const LilvPlugin *Lv2Manager::getPlugin(const QString &uri) { - return getPlugin(std::string(uri.toUtf8())); + return getPlugin(uri.toStdString()); } From 9feb491253a1be73d09bf235a457d38d3e9f8770 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 20:41:50 +0200 Subject: [PATCH 090/120] Lv2Manager::initPlugins: Avoid using bool as int --- src/core/lv2/Lv2Manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index ff423e97198..cf1266645cd 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -101,7 +101,7 @@ void Lv2Manager::initPlugins() m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] = std::move(info); - pluginsLoaded += issues.empty(); + if(issues.empty()) { ++pluginsLoaded; } ++pluginCount; } From 063788fbe844d0e1f79ac16bab6e3c21a294bc17 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 21:17:20 +0200 Subject: [PATCH 091/120] Implement/Use new func stdStringFromPortName --- include/Lv2Basics.h | 8 +++++--- src/core/lv2/Lv2Basics.cpp | 6 ++++++ src/core/lv2/Lv2Ports.cpp | 6 +----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 08e993b6343..0003f83e814 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -34,6 +34,7 @@ #include #include #include +#include struct LilvNodeDeleter { @@ -51,9 +52,7 @@ using AutoLilvNodes = std::unique_ptr; /** Return QString from a plugin's node, everything will be freed automatically @param plug The plugin where the node is - @param getFun The function to return the node from the plugin - @param convFunc convFunc The plugin to return a char pointer from that node; - this is usually lilv_node_as_string or lilv_node_as_uri + @param getFunc The function to return the node from the plugin */ QString qStringFromPluginNode(const LilvPlugin* plug, LilvNode * (*getFunc)(const LilvPlugin*)); @@ -61,5 +60,8 @@ QString qStringFromPluginNode(const LilvPlugin* plug, //! 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 diff --git a/src/core/lv2/Lv2Basics.cpp b/src/core/lv2/Lv2Basics.cpp index 64779823572..b6be53a2cf6 100644 --- a/src/core/lv2/Lv2Basics.cpp +++ b/src/core/lv2/Lv2Basics.cpp @@ -39,5 +39,11 @@ QString qStringFromPortName(const LilvPlugin* plug, const LilvPort* port) lilv_node_as_string(AutoLilvNode(lilv_port_get_name(plug, port)).get())); } +std::string stdStringFromPortName(const LilvPlugin* plug, const LilvPort* port) +{ + return std::string( + lilv_node_as_string(AutoLilvNode(lilv_port_get_name(plug, port)).get())); +} + #endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 9c269bcfa62..b9f84694d69 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -104,11 +104,7 @@ std::vector Meta::get(const LilvPlugin *plugin, auto isA = [&portFunc](const char* str) { return portFunc(lilv_port_is_a, str); }; - std::string portName; - { - AutoLilvNode nameNode(lilv_port_get_name(plugin, lilvPort)); - portName = lilv_node_as_string(nameNode.get()); - } + const std::string portName = stdStringFromPortName(plugin, lilvPort); m_optional = hasProperty(LV2_CORE__connectionOptional); From bc447b3423d656ab7328ae4ee1a2a4d1ff81b4ea Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 21:31:40 +0200 Subject: [PATCH 092/120] Lv2Proc::copyModelsFromCore: Fix cast --- src/core/lv2/Lv2Proc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 6f99db3ccaa..72dbaf316a4 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -141,7 +141,7 @@ void Lv2Proc::copyModelsFromCore() void visit(const IntModel& m) override { m_res = static_cast(m.value()); } void visit(const BoolModel& m) override { - m_res = static_cast(m.value()); } + m_res = static_cast(m.value()); } void visit(const ComboBoxModel& m) override { m_res = (*m_scalePointMap)[static_cast(m.value())]; } }; From 6e2560dff35e3bd1ff9306354b3cfb3ad24c267f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Apr 2020 21:44:50 +0200 Subject: [PATCH 093/120] Lv2ViewProc CTOR: Use plain LilvNode ptr --- src/gui/Lv2ViewBase.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index a562b4dd928..3e540daea0e 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -56,7 +56,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : { public: QWidget* m_par; // input - const AutoLilvNode* m_commentUri; // input + const LilvNode* m_commentUri; // input Control* m_control = nullptr; // output void visit(const Lv2Ports::Control& port) override { @@ -83,7 +83,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : m_control->setText(port.name()); LilvNodes* props = lilv_port_get_value( - port.m_plugin, port.m_port, m_commentUri->get()); + port.m_plugin, port.m_port, m_commentUri); LILV_FOREACH(nodes, itr, props) { const LilvNode* nod = lilv_nodes_get(props, itr); @@ -101,7 +101,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : { SetupWidget setup; setup.m_par = this; - setup.m_commentUri = &commentUri; + setup.m_commentUri = commentUri.get(); port->accept(setup); if (setup.m_control) From d4e5514e489fe94a5ae1b00c85afd07ec05a307c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 21 Apr 2020 18:56:46 +0200 Subject: [PATCH 094/120] Lv2: Remove useless destructors where possible --- plugins/Lv2Effect/Lv2Effect.cpp | 7 ------- plugins/Lv2Effect/Lv2Effect.h | 1 - plugins/Lv2Effect/Lv2FxControlDialog.h | 1 - plugins/Lv2Effect/Lv2FxControls.h | 1 - plugins/Lv2Instrument/Lv2Instrument.cpp | 5 ----- plugins/Lv2Instrument/Lv2Instrument.h | 1 - 6 files changed, 16 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 1e6f8730a01..3811dd747a7 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -61,13 +61,6 @@ Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *ke -Lv2Effect::~Lv2Effect() -{ -} - - - - bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false; } diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index edff320f816..aac88cf93d9 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -37,7 +37,6 @@ class Lv2Effect : public Effect initialization */ Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key* _key); - ~Lv2Effect() override; //! Must be checked after ctor or reload bool isValid() const { return m_controls.isValid(); } diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.h b/plugins/Lv2Effect/Lv2FxControlDialog.h index 74a9273dfa3..3abdb300d4d 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.h +++ b/plugins/Lv2Effect/Lv2FxControlDialog.h @@ -37,7 +37,6 @@ class Lv2FxControlDialog : public EffectControlDialog, public Lv2ViewBase public: Lv2FxControlDialog(Lv2FxControls *controls); - virtual ~Lv2FxControlDialog() override {} private: Lv2FxControls *lv2Controls(); diff --git a/plugins/Lv2Effect/Lv2FxControls.h b/plugins/Lv2Effect/Lv2FxControls.h index ed39c73fddc..e5449a4f7fe 100644 --- a/plugins/Lv2Effect/Lv2FxControls.h +++ b/plugins/Lv2Effect/Lv2FxControls.h @@ -36,7 +36,6 @@ class Lv2FxControls : public EffectControls, public Lv2ControlBase Q_OBJECT public: Lv2FxControls(Lv2Effect *effect, const QString &uri); - ~Lv2FxControls() override {} void saveSettings(QDomDocument &_doc, QDomElement &_parent) override; void loadSettings(const QDomElement &that) override; diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index e1b0704497a..974aaf416b4 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -236,11 +236,6 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : -Lv2InsView::~Lv2InsView() {} - - - - void Lv2InsView::dragEnterEvent(QDragEnterEvent *_dee) { void (QDragEnterEvent::*reaction)(void) = &QDragEnterEvent::ignore; diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index c0d32ee64bc..861482dcd82 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -106,7 +106,6 @@ class Lv2InsView : public InstrumentView, public Lv2ViewBase Q_OBJECT public: Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent); - virtual ~Lv2InsView(); protected: virtual void dragEnterEvent(QDragEnterEvent *_dee); From 193d7555d8d95e527e037e701dfccbe372ac6542 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 21 Apr 2020 19:53:36 +0200 Subject: [PATCH 095/120] Lv2Effect: Fix `checkGate()` call --- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 3811dd747a7..1107484f98d 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -81,7 +81,7 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) double r = static_cast(buf[f][1]); outSum += l*l + r*r; } - checkGate(outSum); + checkGate(outSum / frames); return isRunning(); } From 4eeee2d6ccc328f7655a9b6f3447e674032ada81 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 21 Apr 2020 19:54:48 +0200 Subject: [PATCH 096/120] Lv2Proc CTOR: Remove unreachable else branch --- src/core/lv2/Lv2Proc.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 72dbaf316a4..b27413c0324 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -99,15 +99,7 @@ Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : LinkedModelGroup(parent), m_plugin(plugin) { - if (m_plugin) - { - initPlugin(); - } - else - { - qCritical() << ":-( ! No descriptor found for" << plugin; - m_valid = false; - } + initPlugin(); } From ecfdf3b1609e3b5d2ee2ac24c1f01bbc931f6a44 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 21 Apr 2020 19:56:51 +0200 Subject: [PATCH 097/120] Lv2SubPluginFeatures::listSubPluginKeys: Use auto in for loop --- src/core/lv2/Lv2SubPluginFeatures.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 2728b2d1bb8..3f86c5324e2 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -163,16 +163,15 @@ void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor *desc, KeyList &kl) const { Lv2Manager *lv2Mgr = Engine::getLv2Manager(); - for (const std::pair &pr : - *lv2Mgr) + for (const auto &uriInfoPair : *lv2Mgr) { - if (pr.second.type() == m_type && pr.second.isValid()) + if (uriInfoPair.second.type() == m_type && uriInfoPair.second.isValid()) { using KeyType = Plugin::Descriptor::SubPluginFeatures::Key; KeyType::AttributeMap atm; - atm["uri"] = QString::fromUtf8(pr.first.c_str()); - const LilvPlugin* plug = pr.second.plugin(); + atm["uri"] = QString::fromUtf8(uriInfoPair.first.c_str()); + const LilvPlugin* plug = uriInfoPair.second.plugin(); kl.push_back(KeyType(desc, pluginName(plug), atm)); //qDebug() << "Found LV2 sub plugin key of type" << From 50988831a6ad19a48d67cf6212057f89973286e0 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 21 Apr 2020 20:21:56 +0200 Subject: [PATCH 098/120] Lv2ViewBase: Fix memory leak --- src/gui/Lv2ViewBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 3e540daea0e..14dbf4ad832 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -191,6 +191,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) { grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); } + else { delete btnBox; } m_procView = new Lv2ViewProc(meAsWidget, ctrlBase->control(0), m_colNum); grid->addWidget(m_procView, Rows::ProcRow, 0); From f14918f4e513fc89398734565816bcc3742e6fac Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 21 Apr 2020 20:26:36 +0200 Subject: [PATCH 099/120] Remove useless virtual keywords --- include/Lv2ControlBase.h | 2 +- include/Lv2SubPluginFeatures.h | 2 +- plugins/Lv2Instrument/Lv2Instrument.h | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index cad94fe474b..9f1b54992cd 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -87,7 +87,7 @@ class Lv2ControlBase : public LinkedModelGroups //! 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); - virtual ~Lv2ControlBase() override; + ~Lv2ControlBase() override; //! Must be checked after ctor or reload bool isValid() const { return m_valid; } diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h index c4f14dafb89..33c29c3ef26 100644 --- a/include/Lv2SubPluginFeatures.h +++ b/include/Lv2SubPluginFeatures.h @@ -44,7 +44,7 @@ class Lv2SubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures public: Lv2SubPluginFeatures(Plugin::PluginTypes type); - virtual void fillDescriptionWidget( + void fillDescriptionWidget( QWidget *parent, const Key *k) const override; QString additionalFileExtensions(const Key &k) const override; diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 861482dcd82..6451d49cdcf 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -108,15 +108,15 @@ class Lv2InsView : public InstrumentView, public Lv2ViewBase Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent); protected: - virtual void dragEnterEvent(QDragEnterEvent *_dee); - virtual void dropEvent(QDropEvent *_de); + void dragEnterEvent(QDragEnterEvent *_dee) override; + void dropEvent(QDropEvent *_de) override; private slots: void reloadPlugin(); void toggleUI(); private: - void modelChanged(); + void modelChanged() override; }; From 6919ec13041b7316f51980e069ee652844dda3d9 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 22 Apr 2020 19:40:07 +0200 Subject: [PATCH 100/120] Use AutoLilvNode where possible --- src/core/lv2/Lv2Ports.cpp | 8 ++++---- src/gui/Lv2ViewBase.cpp | 18 ++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index b9f84694d69..1e01eaae8ee 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -137,20 +137,20 @@ std::vector Meta::get(const LilvPlugin *plugin, lilv_port_get_range(plugin, lilvPort, &defN, isToggle ? nullptr : &minN, isToggle ? nullptr : &maxN); + AutoLilvNode def(defN), min(minN), max(maxN); auto takeRangeValue = [&](LilvNode* node, float& storeHere, PluginIssueType it) { if (node) { storeHere = lilv_node_as_float(node); } else { issue(it, portName); } - lilv_node_free(node); }; - takeRangeValue(defN, m_def, portHasNoDef); + takeRangeValue(def.get(), m_def, portHasNoDef); if (!isToggle) { - takeRangeValue(minN, m_min, portHasNoMin); - takeRangeValue(maxN, m_max, portHasNoMax); + takeRangeValue(min.get(), m_min, portHasNoMin); + takeRangeValue(max.get(), m_max, portHasNoMax); if (m_max - m_min > 15.0f) { diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 14dbf4ad832..c1d8a1ac200 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -82,15 +82,14 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : } m_control->setText(port.name()); - LilvNodes* props = lilv_port_get_value( - port.m_plugin, port.m_port, m_commentUri); - LILV_FOREACH(nodes, itr, props) + AutoLilvNodes props(lilv_port_get_value( + port.m_plugin, port.m_port, m_commentUri)); + LILV_FOREACH(nodes, itr, props.get()) { - const LilvNode* nod = lilv_nodes_get(props, itr); + const LilvNode* nod = lilv_nodes_get(props.get(), itr); m_control->topWidget()->setToolTip(lilv_node_as_string(nod)); break; } - lilv_nodes_free(props); } } }; @@ -164,11 +163,11 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) // note: the lifetime of C++ objects ends after the top expression in the // expression syntax tree, so the AutoLilvNode gets freed after the function // has been called - LilvNodes* props = lilv_plugin_get_value(ctrlBase->getPlugin(), - uri(LILV_NS_RDFS "comment").get()); - LILV_FOREACH(nodes, itr, props) + AutoLilvNodes props(lilv_plugin_get_value(ctrlBase->getPlugin(), + uri(LILV_NS_RDFS "comment").get())); + LILV_FOREACH(nodes, itr, props.get()) { - const LilvNode* node = lilv_nodes_get(props, itr); + const LilvNode* node = lilv_nodes_get(props.get(), itr); QLabel* infoLabel = new QLabel(lilv_node_as_string(node)); infoLabel->setWordWrap(true); infoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); @@ -185,7 +184,6 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) break; } - lilv_nodes_free(props); if(m_reloadPluginButton || m_toggleUIButton || m_helpButton) { From 15faa997a899eabb6bb6f23171417b58b8b5c3f2 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 25 Apr 2020 23:12:35 +0200 Subject: [PATCH 101/120] Add one doxygen comment --- include/Lv2Ports.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 17710a59c39..80096e017ba 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -186,6 +186,7 @@ struct Audio : public VisitablePort std::size_t bufferSize() const { return m_buffer.size(); } private: + //! the buffer where Lv2 reads/writes the data from/to std::vector m_buffer; bool m_sidechain; From 4a7f1ce83692782558a0f0c887ab9b5e0f2fd98d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 25 Apr 2020 23:43:04 +0200 Subject: [PATCH 102/120] Implement D/W for Lv2 effects --- plugins/Lv2Effect/Lv2Effect.cpp | 15 +++++++++++---- plugins/Lv2Effect/Lv2Effect.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 1107484f98d..51b3eab5065 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -54,7 +54,8 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *key) : Effect(&lv2effect_plugin_descriptor, parent, key), - m_controls(this, key->attributes["uri"]) + m_controls(this, key->attributes["uri"]), + tmpOutputSmps(Engine::mixer()->framesPerPeriod()) { } @@ -64,6 +65,7 @@ Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *ke bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false; } + Q_ASSERT(frames <= (fpp_t)tmpOutputSmps.size()); m_controls.copyBuffersFromLmms(buf, frames); m_controls.copyModelsFromLmms(); @@ -72,14 +74,19 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) m_controls.run(frames); // m_pluginMutex.unlock(); - m_controls.copyBuffersToLmms(buf, frames); + m_controls.copyBuffersToLmms(tmpOutputSmps.data(), frames); double outSum = .0; + bool corrupt = wetLevel() < 0; // #3261 - if w < 0, bash w := 0, d := 1 + const float d = corrupt ? 1 : dryLevel(); + const float w = corrupt ? 0 : wetLevel(); for(fpp_t f = 0; f < frames; ++f) { - double l = static_cast(buf[f][0]); - double r = static_cast(buf[f][1]); + double l = static_cast(tmpOutputSmps[f][0]); + double r = static_cast(tmpOutputSmps[f][1]); outSum += l*l + r*r; + buf[f][0] = d * buf[f][0] + w * tmpOutputSmps[f][0]; + buf[f][1] = d * buf[f][1] + w * tmpOutputSmps[f][1]; } checkGate(outSum / frames); diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index aac88cf93d9..26b6bee3fcb 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -48,6 +48,7 @@ class Lv2Effect : public Effect private: Lv2FxControls m_controls; + std::vector tmpOutputSmps; }; #endif // LMMS_HAVE_LV2 From 43d758b3463b648aa67d177e9681207781a8b222 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 26 Apr 2020 11:38:07 +0200 Subject: [PATCH 103/120] Distinguish between sidechain and optional ports Before this commit, `Lv2Proc::portIsSideChain` also returned true for connection-optional ports. This has now been split, e.g. there is now `Lv2Proc::portIsOptional`. This is no functional change, it's just refactoring and naming functions right. --- include/Lv2Ports.h | 5 ++++- include/Lv2Proc.h | 1 + src/core/lv2/Lv2Ports.cpp | 4 ++-- src/core/lv2/Lv2Proc.cpp | 31 ++++++++++++++++++++++--------- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 80096e017ba..1378166052e 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -170,7 +170,7 @@ struct Cv : public VisitablePort struct Audio : public VisitablePort { - Audio(std::size_t bufferSize, bool isSidechain); + Audio(std::size_t bufferSize, bool isSidechain, bool isOptional); //! Copy buffer passed by LMMS into our ports void copyBuffersFromCore(const sampleFrame *lmmsBuf, @@ -183,12 +183,15 @@ struct Audio : public VisitablePort unsigned channel, fpp_t frames) const; bool isSideChain() const { return m_sidechain; } + bool isOptional() const { return m_optional; } + bool mustBeUsed() const { return !isSideChain() && !isOptional(); } std::size_t bufferSize() const { return m_buffer.size(); } private: //! the buffer where Lv2 reads/writes the data from/to std::vector m_buffer; bool m_sidechain; + bool m_optional; // the only case when data of m_buffer may be referenced: friend struct ::ConnectPortVisitor; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 48d5fe6da5b..8ad458b0f3a 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -159,6 +159,7 @@ class Lv2Proc : public LinkedModelGroup void dumpPort(std::size_t num); static bool portIsSideChain(const LilvPlugin* plugin, const LilvPort *port); + static bool portIsOptional(const LilvPlugin* plugin, const LilvPort *port); static AutoLilvNode uri(const char* uriStr); }; diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 1e01eaae8ee..6bd5146fc1c 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -197,8 +197,8 @@ QString PortBase::uri() const -Audio::Audio(std::size_t bufferSize, bool isSidechain) - : m_buffer(bufferSize), m_sidechain(isSidechain) +Audio::Audio(std::size_t bufferSize, bool isSidechain, bool isOptional) + : m_buffer(bufferSize), m_sidechain(isSidechain), m_optional(isOptional) { } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index b27413c0324..a839078a8dc 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -52,9 +52,12 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, std::vector tmp = meta.get(plugin, portNum); std::move(tmp.begin(), tmp.end(), std::back_inserter(issues)); - if (meta.m_type == Lv2Ports::Type::Audio && + bool portMustBeUsed = !portIsSideChain(plugin, - lilv_plugin_get_port_by_index(plugin, portNum))) + lilv_plugin_get_port_by_index(plugin, portNum)) && + !portIsOptional(plugin, + lilv_plugin_get_port_by_index(plugin, portNum)); + if (meta.m_type == Lv2Ports::Type::Audio && portMustBeUsed) ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output ? outCount : inCount]; } @@ -351,7 +354,8 @@ void Lv2Proc::createPort(std::size_t portNum) new Lv2Ports::Audio( static_cast( Engine::mixer()->framesPerPeriod()), - portIsSideChain(m_plugin, lilvPort) + portIsSideChain(m_plugin, lilvPort), + portIsOptional(m_plugin, lilvPort) ); port = audio; } else { @@ -394,7 +398,7 @@ void Lv2Proc::createPorts() void visit(Lv2Ports::Audio& audio) override { - if (!audio.isSideChain()) + if (audio.mustBeUsed()) { StereoPortRef dummy; StereoPortRef* portRef = &dummy; @@ -446,8 +450,10 @@ struct ConnectPortVisitor : public Lv2Ports::Visitor static_cast(m_num), location); } void visit(Lv2Ports::Control& ctrl) override { connectPort(&ctrl.m_val); } - void visit(Lv2Ports::Audio& audio) override { - connectPort(audio.isSideChain() ? nullptr : audio.m_buffer.data()); } + void visit(Lv2Ports::Audio& audio) override + { + connectPort((audio.mustBeUsed()) ? audio.m_buffer.data() : nullptr); + } void visit(Lv2Ports::Unknown&) override { connectPort(nullptr); } ~ConnectPortVisitor() override; }; @@ -510,9 +516,16 @@ void Lv2Proc::dumpPort(std::size_t num) bool Lv2Proc::portIsSideChain(const LilvPlugin *plugin, const LilvPort *port) { return lilv_port_has_property(plugin, port, - uri(LV2_CORE_PREFIX "isSidechain").get()) || - lilv_port_has_property(plugin, port, - uri(LV2_CORE__connectionOptional).get()); + uri(LV2_CORE_PREFIX "isSidechain").get()); +} + + + + +bool Lv2Proc::portIsOptional(const LilvPlugin *plugin, const LilvPort *port) +{ + return lilv_port_has_property(plugin, port, + uri(LV2_CORE__connectionOptional).get()); } From 20f9b2c27a114492352ce53b5569bdf56106103c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Tue, 5 May 2020 11:27:38 +0200 Subject: [PATCH 104/120] Lv2ViewBase.cpp: Fix style issues Thanks to @PhysSong for pointing out Co-authored-by: Hyunjin Song --- src/gui/Lv2ViewBase.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index c1d8a1ac200..b8f47eb044b 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -185,7 +185,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) break; } - if(m_reloadPluginButton || m_toggleUIButton || m_helpButton) + if (m_reloadPluginButton || m_toggleUIButton || m_helpButton) { grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); } @@ -207,9 +207,9 @@ Lv2ViewBase::~Lv2ViewBase() { void Lv2ViewBase::toggleHelp(bool visible) { - if ( m_helpWindow ) + if (m_helpWindow) { - if ( visible ) { m_helpWindow->show(); m_helpWindow->raise(); } + if (visible) { m_helpWindow->show(); m_helpWindow->raise(); } else { m_helpWindow->hide(); } } } From cea7f7d8b7e5c8192a37c4c9b16f15a216541ee3 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 5 May 2020 19:40:33 +0200 Subject: [PATCH 105/120] Lv2Effect::tmpOutputSmps: Use 'm_' prefix --- plugins/Lv2Effect/Lv2Effect.cpp | 14 +++++++------- plugins/Lv2Effect/Lv2Effect.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 51b3eab5065..d352c24134b 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -55,7 +55,7 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *key) : Effect(&lv2effect_plugin_descriptor, parent, key), m_controls(this, key->attributes["uri"]), - tmpOutputSmps(Engine::mixer()->framesPerPeriod()) + m_tmpOutputSmps(Engine::mixer()->framesPerPeriod()) { } @@ -65,7 +65,7 @@ Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *ke bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false; } - Q_ASSERT(frames <= (fpp_t)tmpOutputSmps.size()); + Q_ASSERT(frames <= (fpp_t)m_tmpOutputSmps.size()); m_controls.copyBuffersFromLmms(buf, frames); m_controls.copyModelsFromLmms(); @@ -74,7 +74,7 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) m_controls.run(frames); // m_pluginMutex.unlock(); - m_controls.copyBuffersToLmms(tmpOutputSmps.data(), frames); + m_controls.copyBuffersToLmms(m_tmpOutputSmps.data(), frames); double outSum = .0; bool corrupt = wetLevel() < 0; // #3261 - if w < 0, bash w := 0, d := 1 @@ -82,11 +82,11 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) const float w = corrupt ? 0 : wetLevel(); for(fpp_t f = 0; f < frames; ++f) { - double l = static_cast(tmpOutputSmps[f][0]); - double r = static_cast(tmpOutputSmps[f][1]); + double l = static_cast(m_tmpOutputSmps[f][0]); + double r = static_cast(m_tmpOutputSmps[f][1]); outSum += l*l + r*r; - buf[f][0] = d * buf[f][0] + w * tmpOutputSmps[f][0]; - buf[f][1] = d * buf[f][1] + w * tmpOutputSmps[f][1]; + buf[f][0] = d * buf[f][0] + w * m_tmpOutputSmps[f][0]; + buf[f][1] = d * buf[f][1] + w * m_tmpOutputSmps[f][1]; } checkGate(outSum / frames); diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index 26b6bee3fcb..77792d428a1 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -48,7 +48,7 @@ class Lv2Effect : public Effect private: Lv2FxControls m_controls; - std::vector tmpOutputSmps; + std::vector m_tmpOutputSmps; }; #endif // LMMS_HAVE_LV2 From d8e36e58dbd507b03df8acc48435c1916d1736db Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 5 May 2020 19:44:34 +0200 Subject: [PATCH 106/120] Add LMMS_HAVE_SUIL to lmmsconfig.h.in --- src/lmmsconfig.h.in | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 20388b68b3f..86882d22e25 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -14,6 +14,7 @@ #cmakedefine LMMS_HAVE_JACK #cmakedefine LMMS_HAVE_WEAKJACK #cmakedefine LMMS_HAVE_LV2 +#cmakedefine LMMS_HAVE_SUIL #cmakedefine LMMS_HAVE_MP3LAME #cmakedefine LMMS_HAVE_OGGVORBIS #cmakedefine LMMS_HAVE_OSS From 216b68973a1c2e110b184c34efcedab38364dc76 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 5 May 2020 19:46:56 +0200 Subject: [PATCH 107/120] CMakeLists style fixes --- plugins/Lv2Effect/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/Lv2Effect/CMakeLists.txt b/plugins/Lv2Effect/CMakeLists.txt index a0db20656a3..915751797d1 100644 --- a/plugins/Lv2Effect/CMakeLists.txt +++ b/plugins/Lv2Effect/CMakeLists.txt @@ -3,5 +3,7 @@ IF(LMMS_HAVE_LV2) INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) INCLUDE(BuildPlugin) - BUILD_PLUGIN(lv2effect Lv2Effect.cpp Lv2FxControls.cpp Lv2FxControlDialog.cpp Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h MOCFILES Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h EMBEDDED_RESOURCES logo.png) + BUILD_PLUGIN(lv2effect Lv2Effect.cpp Lv2FxControls.cpp Lv2FxControlDialog.cpp Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h + MOCFILES Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h + EMBEDDED_RESOURCES logo.png) ENDIF(LMMS_HAVE_LV2) From a02b1067f959806d7bf2b93925ac9f1339aadec7 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 5 May 2020 19:48:59 +0200 Subject: [PATCH 108/120] Lv2Effect.cpp: Use static_cast --- plugins/Lv2Effect/Lv2Effect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index d352c24134b..43944f7ef3e 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -65,7 +65,7 @@ Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *ke bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false; } - Q_ASSERT(frames <= (fpp_t)m_tmpOutputSmps.size()); + Q_ASSERT(frames <= static_cast(m_tmpOutputSmps.size())); m_controls.copyBuffersFromLmms(buf, frames); m_controls.copyModelsFromLmms(); From 12b2253dd7db9ef91132fe3a864105df8d214137 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 5 May 2020 19:52:59 +0200 Subject: [PATCH 109/120] Remove unnecessary Lv2Ports::Control::accept --- include/Lv2Ports.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 1378166052e..de1f8d1c949 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -150,10 +150,6 @@ struct Control : public VisitablePort //! Model values are being copied here every run //! Between runs, this data is not up-to-date float m_val; - - // overwrite accept() from ControlPortBase - void accept(Visitor& v) override { v.visit(*this); } - void accept(ConstVisitor& v) const override { v.visit(*this); } }; struct Cv : public VisitablePort @@ -162,10 +158,6 @@ struct Cv : public VisitablePort //! Model values are being copied here every run //! Between runs, this data is not up-to-date std::vector m_buffer; - - // overwrite accept() from ControlPortBase - void accept(Visitor& v) override { v.visit(*this); } - void accept(ConstVisitor& v) const override { v.visit(*this); } }; struct Audio : public VisitablePort From c7234aab02827e75f799ee6aee7ad34883a21ea3 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 5 May 2020 20:01:39 +0200 Subject: [PATCH 110/120] Lv2Effect.cpp: Style fixes --- src/core/lv2/Lv2Proc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index a839078a8dc..ef783f62d9d 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -185,9 +185,13 @@ void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, // mono input // (this happens if we have two outputs and only one input) if (inPorts().m_right) + { inPorts().m_right->copyBuffersFromCore(buf, offset + 1, frames); + } else + { inPorts().m_left->addBuffersFromCore(buf, offset + 1, frames); + } } } From 4c2a52a5fc545669e22009de71301c3137cf6a84 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 7 May 2020 20:37:36 +0200 Subject: [PATCH 111/120] Lv2Effect: Fix calling checkGate with only wet --- plugins/Lv2Effect/Lv2Effect.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index 43944f7ef3e..2ee5ceabbe5 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -82,11 +82,11 @@ bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) const float w = corrupt ? 0 : wetLevel(); for(fpp_t f = 0; f < frames; ++f) { - double l = static_cast(m_tmpOutputSmps[f][0]); - double r = static_cast(m_tmpOutputSmps[f][1]); - outSum += l*l + r*r; buf[f][0] = d * buf[f][0] + w * m_tmpOutputSmps[f][0]; buf[f][1] = d * buf[f][1] + w * m_tmpOutputSmps[f][1]; + double l = static_cast(buf[f][0]); + double r = static_cast(buf[f][1]); + outSum += l*l + r*r; } checkGate(outSum / frames); From 13d39d2918a58de9d68dd0a9618a0824e815bc47 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 7 May 2020 20:39:34 +0200 Subject: [PATCH 112/120] Lv2ViewBase: Remove useless UI hint --- src/gui/Lv2ViewBase.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index b8f47eb044b..9b9217e48b2 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -151,9 +151,6 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) m_toggleUIButton->setIcon(embed::getIconPixmap("zoom")); m_toggleUIButton->setFont( pointSize<8>(m_toggleUIButton->font())); - m_toggleUIButton->setWhatsThis( - QObject::tr("Click here to show or hide the " - "Lv2 graphical user interface (GUI).")); btnBox->addWidget(m_toggleUIButton, 0); } btnBox->addStretch(1); From 5c6ec8a5397ff432826e665aaeb4ec5f7e6f59bf Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 7 May 2020 20:44:05 +0200 Subject: [PATCH 113/120] Lv2Ports::Audio: Rename a function --- include/Lv2Ports.h | 2 +- src/core/lv2/Lv2Ports.cpp | 2 +- src/core/lv2/Lv2Proc.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index de1f8d1c949..56274d4b6d2 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -168,7 +168,7 @@ struct Audio : public VisitablePort void copyBuffersFromCore(const sampleFrame *lmmsBuf, unsigned channel, fpp_t frames); //! Add buffer passed by LMMS into our ports, and halve the result - void addBuffersFromCore(const sampleFrame *lmmsBuf, + void averageWithBuffersFromCore(const sampleFrame *lmmsBuf, unsigned channel, fpp_t frames); //! Copy our ports into buffers passed by LMMS void copyBuffersToCore(sampleFrame *lmmsBuf, diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 6bd5146fc1c..ae2d26d4971 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -217,7 +217,7 @@ void Audio::copyBuffersFromCore(const sampleFrame *lmmsBuf, -void Audio::addBuffersFromCore(const sampleFrame *lmmsBuf, +void Audio::averageWithBuffersFromCore(const sampleFrame *lmmsBuf, unsigned channel, fpp_t frames) { for (std::size_t f = 0; f < static_cast(frames); ++f) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index ef783f62d9d..74f784560e4 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -190,7 +190,7 @@ void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, } else { - inPorts().m_left->addBuffersFromCore(buf, offset + 1, frames); + inPorts().m_left->averageWithBuffersFromCore(buf, offset + 1, frames); } } } From 74c92c34bde117614040a0f08f89a88ab2a3746e Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 7 May 2020 21:32:54 +0200 Subject: [PATCH 114/120] Lv2Proc: Improve docs for 1st channel offsets --- include/Lv2Ports.h | 3 +++ include/Lv2Proc.h | 32 ++++++++++++++++++++++++-------- src/core/lv2/Lv2ControlBase.cpp | 12 ++++++------ src/core/lv2/Lv2Proc.cpp | 14 +++++++------- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 56274d4b6d2..c9e6c16c855 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -165,12 +165,15 @@ struct Audio : public VisitablePort Audio(std::size_t bufferSize, bool isSidechain, bool isOptional); //! Copy buffer passed by LMMS into our ports + //! @param channel channel index into each sample frame void copyBuffersFromCore(const sampleFrame *lmmsBuf, unsigned channel, fpp_t frames); //! Add buffer passed by LMMS into our ports, and halve the result + //! @param channel channel index into each sample frame void averageWithBuffersFromCore(const sampleFrame *lmmsBuf, unsigned channel, fpp_t frames); //! Copy our ports into buffers passed by LMMS + //! @param channel channel index into each sample frame void copyBuffersToCore(sampleFrame *lmmsBuf, unsigned channel, fpp_t frames) const; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 8ad458b0f3a..fb1f3466634 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -107,15 +107,31 @@ class Lv2Proc : public LinkedModelGroup */ //! Copy values from all connected models into the respective ports void copyModelsFromCore(); - //! Copy buffer passed by the core into our ports - //! @param offset Offset in each frame of @p buf to take - //! @param num Number of buffers provided in @param buf + /** + * Copy buffer passed by the core into our ports + * @param buf buffer of sample frames, each sample frame is something like + * a `float[ * ]` array. + * @param firstChan The offset for @p buf where we have to read our + * first channel. + * This marks the first sample in each sample frame where we read from. + * If we are the 2nd of 2 mono procs, this can be greater than 0. + * @param num Number of channels we must read from @param buf (starting at + * @p offset) + */ void copyBuffersFromCore(const sampleFrame *buf, - unsigned offset, unsigned num, fpp_t frames); - //! Copy our ports into buffers passed by the core - //! @param offset Offset in each frame of @p buf to fill - //! @param num Number of buffers in @param buf we must fill - void copyBuffersToCore(sampleFrame *buf, unsigned offset, unsigned num, + unsigned firstChan, unsigned num, fpp_t frames); + /** + * Copy our ports into buffers passed by the core + * @param buf buffer of sample frames, each sample frame is something like + * a `float[ * ]` array. + * @param firstChan The offset for @p buf where we have to write our + * first channel. + * This marks the first sample in each sample frame where we write to. + * If we are the 2nd of 2 mono procs, this can be greater than 0. + * @param num Number of channels we must write to @param buf (starting at + * @p offset) + */ + void copyBuffersToCore(sampleFrame *buf, unsigned firstChan, unsigned num, fpp_t frames) const; //! Run the Lv2 plugin instance for @param frames frames void run(fpp_t frames); diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 84335415b60..3f50325e7d2 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -115,10 +115,10 @@ void Lv2ControlBase::copyModelsFromLmms() { void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { - unsigned offset = 0; + unsigned firstChan = 0; // tell the procs which channels they shall read from for (auto& c : m_procs) { - c->copyBuffersFromCore(buf, offset, m_channelsPerProc, frames); - offset += m_channelsPerProc; + c->copyBuffersFromCore(buf, firstChan, m_channelsPerProc, frames); + firstChan += m_channelsPerProc; } } @@ -126,10 +126,10 @@ void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { - unsigned offset = 0; + unsigned firstChan = 0; // tell the procs which channels they shall write to for (const auto& c : m_procs) { - c->copyBuffersToCore(buf, offset, m_channelsPerProc, frames); - offset += m_channelsPerProc; + c->copyBuffersToCore(buf, firstChan, m_channelsPerProc, frames); + firstChan += m_channelsPerProc; } } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 74f784560e4..2d77f3f9835 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -174,10 +174,10 @@ void Lv2Proc::copyModelsFromCore() void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, - unsigned offset, unsigned num, + unsigned firstChan, unsigned num, fpp_t frames) { - inPorts().m_left->copyBuffersFromCore(buf, offset, frames); + inPorts().m_left->copyBuffersFromCore(buf, firstChan, frames); if (num > 1) { // if the caller requests to take input from two channels, but we only @@ -186,11 +186,11 @@ void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, // (this happens if we have two outputs and only one input) if (inPorts().m_right) { - inPorts().m_right->copyBuffersFromCore(buf, offset + 1, frames); + inPorts().m_right->copyBuffersFromCore(buf, firstChan + 1, frames); } else { - inPorts().m_left->averageWithBuffersFromCore(buf, offset + 1, frames); + inPorts().m_left->averageWithBuffersFromCore(buf, firstChan + 1, frames); } } } @@ -199,10 +199,10 @@ void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, void Lv2Proc::copyBuffersToCore(sampleFrame* buf, - unsigned offset, unsigned num, + unsigned firstChan, unsigned num, fpp_t frames) const { - outPorts().m_left->copyBuffersToCore(buf, offset + 0, frames); + outPorts().m_left->copyBuffersToCore(buf, firstChan + 0, frames); if (num > 1) { // if the caller requests to copy into two channels, but we only have @@ -210,7 +210,7 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf, // (this happens if we have two inputs and only one output) Lv2Ports::Audio* ap = outPorts().m_right ? outPorts().m_right : outPorts().m_left; - ap->copyBuffersToCore(buf, offset + 1, frames); + ap->copyBuffersToCore(buf, firstChan + 1, frames); } } From bca38f0dee35de177e9e6c8e104d6756dfdffaf9 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 10 May 2020 19:35:47 +0200 Subject: [PATCH 115/120] Enable turning on/off Lv2 debug output --- include/Lv2Manager.h | 1 + src/core/lv2/Lv2Manager.cpp | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 609f684b98c..8437715619e 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -115,6 +115,7 @@ class Lv2Manager Iterator end() { return m_lv2InfoMap.end(); } private: + bool m_debug; //!< if set, debug output will be printed LilvWorld* m_world; Lv2InfoMap m_lv2InfoMap; bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index cf1266645cd..bce3bf372ca 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -26,6 +26,7 @@ #ifdef LMMS_HAVE_LV2 +#include #include #include #include @@ -44,6 +45,9 @@ Lv2Manager::Lv2Manager() { + const char* dbgStr = getenv("LMMS_LV2_DEBUG"); + m_debug = (dbgStr && *dbgStr); + m_world = lilv_world_new(); lilv_world_load_all(m_world); } @@ -96,7 +100,7 @@ void Lv2Manager::initPlugins() const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); std::vector issues; - Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues, true); + Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues, m_debug); Lv2Info info(curPlug, type, issues.empty()); m_lv2InfoMap[lilv_node_as_uri(lilv_plugin_get_uri(curPlug))] @@ -108,6 +112,22 @@ void Lv2Manager::initPlugins() qDebug() << "Lv2 plugin SUMMARY:" << pluginsLoaded << "of" << pluginCount << " loaded in" << timer.elapsed() << "msecs."; + if(pluginsLoaded != pluginCount) + { + if (m_debug) + { + qDebug() << + "If you don't want to see all this debug output, please set\n" + " environment variable \"LMMS_LV2_DEBUG\" to empty or\n" + " do not set it."; + } + else + { + qDebug() << + "For details about not loaded plugins, please set\n" + " environment variable \"LMMS_LV2_DEBUG\" to nonempty."; + } + } } From e443ce115a94f442364c6096ffd46f4e758c515b Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 17 May 2020 08:11:20 +0200 Subject: [PATCH 116/120] Apply suggestions from code review Fix CMakeLists.txt issues, thanks to @PhysSong Co-authored-by: Hyunjin Song --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b210e657a3..085b720d9ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,7 +188,7 @@ IF(WANT_LV2) IF(PKG_CONFIG_FOUND) PKG_CHECK_MODULES(LV2 lv2) PKG_CHECK_MODULES(LILV lilv-0) - IF(${LV2_FOUND} AND ${LILV_FOUND}) + IF(LV2_FOUND AND LILV_FOUND) SET(LMMS_HAVE_LV2 TRUE) SET(STATUS_LV2 "OK") ELSE() @@ -204,7 +204,7 @@ ENDIF(WANT_LV2) IF(WANT_SUIL) IF(PKG_CONFIG_FOUND) PKG_CHECK_MODULES(SUIL suil-0) - IF(${SUIL_FOUND}) + IF(SUIL_FOUND) SET(LMMS_HAVE_SUIL TRUE) SET(STATUS_SUIL "OK") ELSE() From 39bfecdf6bb5d90c5e722606cdd0a824b9bfd22d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Tue, 19 May 2020 07:21:17 +0200 Subject: [PATCH 117/120] CMakeLists.txt: Improve `WANT_SUIL` description Co-authored-by: Hyunjin Song --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cc74e84f4b..36e3a699b2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ 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 plugin UIs" 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) From 3862929d99e70fa5d3e7ab04528937d385d6d674 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 21 May 2020 19:08:01 +0200 Subject: [PATCH 118/120] Remove Lv2ViewProc::leaveEvent --- include/Lv2ViewBase.h | 1 - src/gui/Lv2ViewBase.cpp | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index 88d35dcc8ef..980e1f7efcf 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -49,7 +49,6 @@ class Lv2ViewProc : public LinkedModelGroupView ~Lv2ViewProc(); private: - void leaveEvent(class QEvent *) override; static AutoLilvNode uri(const char *uriStr); }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 423d074c3f1..9b9217e48b2 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -122,17 +122,6 @@ Lv2ViewProc::~Lv2ViewProc() {} -void Lv2ViewProc::leaveEvent(QEvent *) -{ - if(!rect().contains(mapFromGlobal(QCursor::pos()))) - { - removeFocusFromSearchBar(); - } -} - - - - AutoLilvNode Lv2ViewProc::uri(const char *uriStr) { return Engine::getLv2Manager()->uri(uriStr); From 756b84ec5f721ce0323e354ec9fa8f97b49724da Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 23 May 2020 09:12:39 +0200 Subject: [PATCH 119/120] Revert "Remove Lv2ViewProc::leaveEvent" This reverts commit 3862929d99e70fa5d3e7ab04528937d385d6d674. --- include/Lv2ViewBase.h | 1 + src/gui/Lv2ViewBase.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index 980e1f7efcf..88d35dcc8ef 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -49,6 +49,7 @@ class Lv2ViewProc : public LinkedModelGroupView ~Lv2ViewProc(); private: + void leaveEvent(class QEvent *) override; static AutoLilvNode uri(const char *uriStr); }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 9b9217e48b2..423d074c3f1 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -122,6 +122,17 @@ Lv2ViewProc::~Lv2ViewProc() {} +void Lv2ViewProc::leaveEvent(QEvent *) +{ + if(!rect().contains(mapFromGlobal(QCursor::pos()))) + { + removeFocusFromSearchBar(); + } +} + + + + AutoLilvNode Lv2ViewProc::uri(const char *uriStr) { return Engine::getLv2Manager()->uri(uriStr); From 5602bb9fef7ec1e795abacb2b8c154099c578376 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 23 May 2020 19:07:12 +0200 Subject: [PATCH 120/120] Revert "Revert "Remove Lv2ViewProc::leaveEvent"" This reverts commit 756b84ec5f721ce0323e354ec9fa8f97b49724da. --- include/Lv2ViewBase.h | 1 - src/gui/Lv2ViewBase.cpp | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index 88d35dcc8ef..980e1f7efcf 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -49,7 +49,6 @@ class Lv2ViewProc : public LinkedModelGroupView ~Lv2ViewProc(); private: - void leaveEvent(class QEvent *) override; static AutoLilvNode uri(const char *uriStr); }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 423d074c3f1..9b9217e48b2 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -122,17 +122,6 @@ Lv2ViewProc::~Lv2ViewProc() {} -void Lv2ViewProc::leaveEvent(QEvent *) -{ - if(!rect().contains(mapFromGlobal(QCursor::pos()))) - { - removeFocusFromSearchBar(); - } -} - - - - AutoLilvNode Lv2ViewProc::uri(const char *uriStr) { return Engine::getLv2Manager()->uri(uriStr);