diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h deleted file mode 100644 index cf5aabd0d46..00000000000 --- a/include/LinkedModelGroupViews.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * LinkedModelGroupViews.h - view 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 LMMS_GUI_LINKED_MODEL_GROUP_VIEWS_H -#define LMMS_GUI_LINKED_MODEL_GROUP_VIEWS_H - -#include -#include -#include - -namespace lmms -{ - - -class LinkedModelGroup; -class LinkedModelGroups; - - -namespace gui -{ - -class Control; - -/** - @file LinkedModelGroupViews.h - See Lv2ViewBase.h for example usage -*/ - - -/** - 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. -*/ -class LinkedModelGroupView : public QWidget -{ -public: - /** - @param colNum numbers of columns for the controls - (link LEDs not counted) - */ - LinkedModelGroupView(QWidget* parent, LinkedModelGroup* model, - std::size_t colNum); - ~LinkedModelGroupView() override = default; - - //! Reconnect models if model changed - void modelChanged(LinkedModelGroup* linkedModelGroup); - -protected: - //! Add a control to this widget - //! @warning This widget will own this control, do not free it - void addControl(Control* ctrl, const std::string &id, - const std::string& display, bool removable); - - void removeControl(const QString &key); - - void removeFocusFromSearchBar(); - -private: - class LinkedModelGroup* m_model; - - //! column number in surrounding grid in LinkedModelGroupsView - std::size_t m_colNum; - class ControlLayout* m_layout; - std::map> m_widgets; -}; - - -/** - 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 -*/ -class LinkedModelGroupsView -{ -protected: - ~LinkedModelGroupsView() = default; - - //! Reconnect models if model changed; to be called by child virtuals - void modelChanged(LinkedModelGroups* ctrlBase); - -private: - //! The base class must return the addressed group view, - //! which has the same value as "this" - virtual LinkedModelGroupView* getGroupView() = 0; -}; - - -} // namespace gui - -} // namespace lmms - -#endif // LMMS_GUI_LINKED_MODEL_GROUP_VIEWS_H diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h deleted file mode 100644 index c537d7fca2d..00000000000 --- a/include/LinkedModelGroups.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * LinkedModelGroups.h - base classes for groups of linked 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 LMMS_LINKED_MODEL_GROUPS_H -#define LMMS_LINKED_MODEL_GROUPS_H - -#include - -#include "Model.h" - -class QDomDocument; -class QDomElement; - -namespace lmms -{ - -/** - @file LinkedModelGroups.h - See Lv2ControlBase.h and Lv2Proc.h for example usage -*/ - - -/** - Base class for a group of linked models - - See the LinkedModelGroup class for explanations - - Features: - * Models are stored by their QObject::objectName - * Models are linked automatically -*/ -class LinkedModelGroup : public Model -{ - Q_OBJECT -public: - /* - Initialization - */ - //! @param parent model of the LinkedModelGroups class - LinkedModelGroup(Model* parent) : Model(parent) {} - - /* - Linking (initially only) - */ - void linkControls(LinkedModelGroup *other); - - /* - Models - */ - struct ModelInfo - { - QString m_name; - class AutomatableModel* m_model; - ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? - ModelInfo(const QString& name, AutomatableModel* model) - : m_name(name), m_model(model) {} - }; - - // TODO: refactor those 2 - template - void foreach_model(const Functor& ftor) - { - for (auto itr = m_models.begin(); itr != m_models.end(); ++itr) - { - ftor(itr->first, itr->second); - } - } - - template - void foreach_model(const Functor& ftor) const - { - for (auto itr = m_models.cbegin(); itr != m_models.cend(); ++itr) - { - ftor(itr->first, itr->second); - } - } - - std::size_t modelNum() const { return m_models.size(); } - bool containsModel(const QString& name) const; - void removeControl(AutomatableModel *); - - /* - Load/Save - */ - void saveValues(class QDomDocument& doc, class QDomElement& that); - void loadValues(const class QDomElement& that); - -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(lmms::AutomatableModel* added); - void modelRemoved(lmms::AutomatableModel* removed); - -public: - 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(); - -private: - //! models for the controls - //! @note The AutomatableModels behind the ModelInfo are not owned, - //! but referenced after `addModel` is being called. - std::map m_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 always linked. - - A typical application are two mono plugins making a stereo plugin. - - @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. -*/ -class LinkedModelGroups -{ -public: - virtual ~LinkedModelGroups() = default; - - void linkAllModels(); - - /* - 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; - //! @see getGroup - virtual const LinkedModelGroup* getGroup(std::size_t idx) const = 0; -}; - - -} // namespace lmms - -#endif // LMMS_LINKED_MODEL_GROUPS_H diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h deleted file mode 100644 index 8ee235ad898..00000000000 --- a/include/Lv2ControlBase.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Lv2ControlBase.h - Lv2 control base class - * - * Copyright (c) 2018-2023 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 LMMS_LV2_CONTROL_BASE_H -#define LMMS_LV2_CONTROL_BASE_H - -#include "lmmsconfig.h" - -#ifdef LMMS_HAVE_LV2 - -#include -#include - -#include "DataFile.h" -#include "LinkedModelGroups.h" -#include "lmms_export.h" -#include "Plugin.h" - -namespace lmms -{ - - -class Lv2Proc; -class PluginIssue; -class SampleFrame; - -/** - 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: - * inheriting 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 LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups -{ -public: - static Plugin::Type check(const LilvPlugin* m_plugin, - std::vector &issues); - - void shutdown(); - void init(Model* meAsModel); - - const LilvPlugin* getPlugin() const { return m_plugin; } - - Lv2Proc *control(std::size_t idx) { return m_procs[idx].get(); } - const Lv2Proc *control(std::size_t idx) const { return m_procs[idx].get(); } - - bool hasGui() const { return m_hasGUI; } - void setHasGui(bool val) { m_hasGUI = val; } - -protected: - /* - ctor/dtor - */ - //! @param that the class inheriting this class and inheriting Model; - //! this is the same pointer as this, but a different type - //! @param uri the Lv2 URI telling this class what plugin to construct - Lv2ControlBase(class Model *that, const QString& uri); - Lv2ControlBase(const Lv2ControlBase&) = delete; - ~Lv2ControlBase() override; - void reload(); - - Lv2ControlBase& operator=(const Lv2ControlBase&) = delete; - - /* - overrides - */ - LinkedModelGroup* getGroup(std::size_t idx) override; - const LinkedModelGroup* getGroup(std::size_t idx) const override; - - /* - utils for the run thread - */ - //! Copy values from the LMMS core (connected models, MIDI events, ...) into - //! the respective ports - void copyModelsFromLmms(); - //! Bring values from all ports to the LMMS core - void copyModelsToLmms() const; - - //! Copy buffer passed by LMMS into our ports - void copyBuffersFromLmms(const SampleFrame* buf, fpp_t frames); - //! Copy our ports into buffers passed by LMMS - void copyBuffersToLmms(SampleFrame* buf, fpp_t frames) const; - //! Run the Lv2 plugin instance for @param frames frames - void run(fpp_t frames); - - /* - load/save, must be called from virtuals - */ - void saveSettings(QDomDocument &doc, QDomElement &that); - void loadSettings(const QDomElement &that); - void loadFile(const QString &file); - - /* - more functions that must be called from virtuals - */ - std::size_t controlCount() const; - QString nodeName() const { return "lv2controls"; } - bool hasNoteInput() const; - void handleMidiInputEvent(const class MidiEvent &event, - const class TimePos &time, f_cnt_t offset); - -private: - //! 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_hasGUI = false; - unsigned m_channelsPerProc; - - const LilvPlugin* m_plugin; -}; - - -} // namespace lmms - -#endif // LMMS_HAVE_LV2 - -#endif // LMMS_LV2_CONTROL_BASE_H diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index f315c5d7a89..53bb0ad66f6 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -1,7 +1,7 @@ /* * Lv2Proc.h - Lv2 processor class * - * Copyright (c) 2019-2022 Johannes Lorenz + * Copyright (c) 2019-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -35,7 +35,7 @@ #include -#include "LinkedModelGroups.h" +#include "ModelGroup.h" #include "LmmsSemaphore.h" #include "Lv2Basics.h" #include "Lv2Features.h" @@ -48,6 +48,8 @@ namespace lmms { +class AutomatableModel; +class MidiEvent; class PluginIssue; class SampleFrame; @@ -63,10 +65,32 @@ namespace Lv2Ports 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 +/** + Class representing one Lv2 processor, i.e. one Lv2 handle. + For mono plugins, L/R channel routing is being used + (this includes techniques like upmixing and downmixing). + + This class provides everything Lv2 effect and instrument 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: + * inheriting 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 LMMS_EXPORT Lv2Proc : public ModelGroup { friend class Lv2ProcSuspender; public: @@ -76,8 +100,8 @@ class Lv2Proc : public LinkedModelGroup /* ctor/dtor/reload */ - Lv2Proc(const LilvPlugin* plugin, Model *parent); - ~Lv2Proc() override; + Lv2Proc(Model *parent, const QString &uri); + ~Lv2Proc(); void reload(); void onSampleRateChanged(); @@ -128,51 +152,37 @@ class Lv2Proc : public LinkedModelGroup * 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 firstChan, unsigned num, fpp_t frames); + void copyBuffersFromCore(const SampleFrame* buf, 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; + void copyBuffersToCore(SampleFrame* buf, fpp_t frames) const; //! Run the Lv2 plugin instance for @param frames frames void run(fpp_t frames); - void handleMidiInputEvent(const class MidiEvent &event, + void handleMidiInputEvent(const MidiEvent &event, const TimePos &time, f_cnt_t offset); /* misc */ - class AutomatableModel *modelAtPort(const QString &uri); // unused currently - std::size_t controlCount() const { return LinkedModelGroup::modelNum(); } + AutomatableModel *modelAtPort(const QString &uri); // unused currently + std::size_t controlCount() const { return ModelGroup::size(); } bool hasNoteInput() const; + const LilvPlugin* getPlugin() const { return m_plugin; } + bool hasGui() const { return m_hasGUI; } protected: - /* - load and save - */ - //! Create ports and instance, connect ports, activate plugin - void initPlugin(); - //! Deactivate instance - void shutdownPlugin(); + //! To be called by `Plugin::loadFile` overrides + void loadFile(const QString &file) { (void)file; } + //! XML DOM Node name + QString nodeName() const { return "lv2controls"; } private: + // lv2 const LilvPlugin* m_plugin; LilvInstance* m_instance = nullptr; Lv2Features m_features; @@ -203,6 +213,7 @@ class Lv2Proc : public LinkedModelGroup // other static int32_t defaultEvbufSize() { return 1 << 15; /* ardour uses this*/ } + bool m_hasGUI = false; //! models for the controls, sorted by port symbols //! @note These are not owned, but rather link to the models in @@ -212,6 +223,10 @@ class Lv2Proc : public LinkedModelGroup void initMOptions(); //!< initialize m_options void initPluginSpecificFeatures(); + //! Create ports and instance, connect ports, activate plugin + void initPlugin(); + //! Deactivate instance + void shutdownPlugin(); //! 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 diff --git a/include/Lv2ViewBase.h b/include/Lv2ProcView.h similarity index 73% rename from include/Lv2ViewBase.h rename to include/Lv2ProcView.h index 43086849cb6..ae8c57c6bab 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ProcView.h @@ -1,7 +1,7 @@ /* * Lv2ViewBase.h - base class for Lv2 plugin views * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -25,12 +25,18 @@ #ifndef LMMS_GUI_LV2_VIEW_BASE_H #define LMMS_GUI_LV2_VIEW_BASE_H +#include "Lv2Proc.h" #include "lmmsconfig.h" #ifdef LMMS_HAVE_LV2 -#include "LinkedModelGroupViews.h" +#include +namespace lmms::gui { +class Control; +} + +#include "ModelGroupView.h" #include "lmms_export.h" #include "Lv2Basics.h" @@ -51,42 +57,28 @@ namespace gui class LedCheckBox; -//! View for one processor, Lv2ViewBase contains 2 of those for mono plugins -class Lv2ViewProc : public LinkedModelGroupView -{ -public: - //! @param colNum numbers of columns for the controls - Lv2ViewProc(QWidget *parent, Lv2Proc *proc, int colNum); - ~Lv2ViewProc() override = default; - -private: - static AutoLilvNode uri(const char *uriStr); -}; - - - class HelpWindowEventFilter : public QObject { Q_OBJECT - class Lv2ViewBase* const m_viewBase; + class Lv2ProcView* const m_procView; protected: bool eventFilter(QObject* obj, QEvent* event) override; public: - HelpWindowEventFilter(class Lv2ViewBase* viewBase); + HelpWindowEventFilter(class Lv2ProcView* viewBase); }; //! Base class for view for one Lv2 plugin -class LMMS_EXPORT Lv2ViewBase : public LinkedModelGroupsView +class LMMS_EXPORT Lv2ProcView : public ModelGroupView { friend class HelpWindowEventFilter; protected: //! @param pluginWidget A child class which inherits QWidget - Lv2ViewBase(class QWidget *pluginWidget, Lv2ControlBase *ctrlBase); - ~Lv2ViewBase(); + Lv2ProcView(class QWidget *pluginWidget, Lv2Proc *proc); + ~Lv2ProcView(); // these widgets must be connected by child widgets QPushButton* m_reloadPluginButton = nullptr; @@ -99,7 +91,7 @@ class LMMS_EXPORT Lv2ViewBase : public LinkedModelGroupsView // to be called by child virtuals //! Reconnect models if model changed - void modelChanged(Lv2ControlBase* ctrlBase); + void modelChanged(Lv2Proc *proc); private: enum Rows @@ -110,11 +102,8 @@ class LMMS_EXPORT Lv2ViewBase : public LinkedModelGroupsView }; static AutoLilvNode uri(const char *uriStr); - LinkedModelGroupView* getGroupView() override { return m_procView; } void onHelpWindowClosed(); - Lv2ViewProc* m_procView; - //! Numbers of controls per row; must be multiple of 2 for mono effects const int m_colNum = 6; QMdiSubWindow* m_helpWindow = nullptr; diff --git a/include/ModelGroup.h b/include/ModelGroup.h new file mode 100644 index 00000000000..f5d2c18dc83 --- /dev/null +++ b/include/ModelGroup.h @@ -0,0 +1,112 @@ +/* + * ModelGroup.h - base classes for groups of models + * + * Copyright (c) 2019-2024 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 LMMS_MODEL_GROUPS_H +#define LMMS_MODEL_GROUPS_H + +#include + +#include "Model.h" + +class QDomDocument; +class QDomElement; + +namespace lmms { + +/** + @file ModelGroup.h + See Lv2Proc.h for example usage +*/ + +class AutomatableModel; + +/** + Base class for a group of models + + Features: + * Models are stored by their QObject::objectName + * Load/Save routines + * Add/Remove routines +*/ +class LMMS_EXPORT ModelGroup +{ +public: + // models + struct ModelInfo + { + QString m_name; + AutomatableModel* m_model; + ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? + ModelInfo(const QString& name, AutomatableModel* model) + : m_name(name) + , m_model(model) + { + } + }; + + // TODO: refactor those 2 + template void foreach_model(const Functor& ftor) + { + for (auto& [name, info] : m_models) + { + ftor(name, info); + } + } + + template void foreach_model(const Functor& ftor) const + { + for (const auto& [name, info] : m_models) + { + ftor(name, info); + } + } + +protected: + ModelGroup(Model* parent) + : m_parent(parent) + { + } + + void saveSettings(QDomDocument& doc, QDomElement& that); + void loadSettings(const QDomElement& that); + + std::size_t size() const { return m_models.size(); } + + //! Register a further model + void addModel(AutomatableModel* model, const QString& name); + //! Remove a model + void removeModel(AutomatableModel*); + +private: + //! models for the controls + //! @note The AutomatableModels behind the ModelInfo are not owned, + //! but referenced after `addModel` is being called. + std::map m_models; + + Model* m_parent; +}; + +} // namespace lmms + +#endif // LMMS_MODEL_GROUPS_H diff --git a/include/ModelGroupView.h b/include/ModelGroupView.h new file mode 100644 index 00000000000..d8a981209f6 --- /dev/null +++ b/include/ModelGroupView.h @@ -0,0 +1,81 @@ +/* + * ModelGroupView.h - view for groups of models + * + * Copyright (c) 2019-2024 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 LMMS_GUI_MODEL_GROUP_VIEWS_H +#define LMMS_GUI_MODEL_GROUP_VIEWS_H + +#include +#include +#include +#include + +class QString; +class QWidget; + +namespace lmms { + +class ModelGroup; + +namespace gui { + +class Control; +class ControlLayout; + +/** + @file ModelGroupView.h + See Lv2ViewBase.h for example usage +*/ + +class ModelGroupView +{ +public: + //! @param colNum numbers of columns for the controls + ModelGroupView(QWidget* parent, ModelGroup* model); + + //! Reconnect models if model changed + void modelChanged(ModelGroup* modelGroup); + +protected: + //! Add a control to this widget + //! @warning This widget will own this control, do not free it + void addControl(QWidget* parent, Control* ctrl, const std::string& id, const std::string& display, bool removable); + + void removeControl(const QString& key); + + void removeFocusFromSearchBar(); + +private: + ModelGroup* m_model; + + //! column number in surrounding grid in ModelGroupView + std::size_t m_colNum; + ControlLayout* m_layout; + std::map> m_widgets; +}; + +} // namespace gui + +} // namespace lmms + +#endif // LMMS_GUI_MODEL_GROUP_VIEWS_H diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index afa69fe1373..e0344bc450e 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -1,7 +1,7 @@ /* * Lv2Effect.cpp - implementation of LV2 effect * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -72,15 +72,15 @@ Effect::ProcessStatus Lv2Effect::processImpl(SampleFrame* buf, const fpp_t frame { Q_ASSERT(frames <= static_cast(m_tmpOutputSmps.size())); - m_controls.copyBuffersFromLmms(buf, frames); - m_controls.copyModelsFromLmms(); + m_controls.copyBuffersFromCore(buf, frames); + m_controls.copyModelsFromCore(); // m_pluginMutex.lock(); m_controls.run(frames); // m_pluginMutex.unlock(); - m_controls.copyModelsToLmms(); - m_controls.copyBuffersToLmms(m_tmpOutputSmps.data(), frames); + m_controls.copyModelsToCore(); + m_controls.copyBuffersToCore(m_tmpOutputSmps.data(), frames); bool corrupt = wetLevel() < 0; // #3261 - if w < 0, bash w := 0, d := 1 const float d = corrupt ? 1 : dryLevel(); diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.cpp b/plugins/Lv2Effect/Lv2FxControlDialog.cpp index 73890937c04..843928143b5 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.cpp +++ b/plugins/Lv2Effect/Lv2FxControlDialog.cpp @@ -1,7 +1,7 @@ /* * Lv2FxControlDialog.cpp - Lv2FxControlDialog implementation * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -34,7 +34,7 @@ namespace lmms::gui Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) : EffectControlDialog(controls), - Lv2ViewBase(this, controls) + Lv2ProcView(this, controls) { if (m_reloadPluginButton) { connect(m_reloadPluginButton, &QPushButton::clicked, @@ -66,7 +66,7 @@ Lv2FxControls *Lv2FxControlDialog::lv2Controls() void Lv2FxControlDialog::modelChanged() { - Lv2ViewBase::modelChanged(lv2Controls()); + Lv2ProcView::modelChanged(lv2Controls()); connect(lv2Controls(), &Lv2FxControls::modelChanged, this, [this](){ this->modelChanged();} ); } diff --git a/plugins/Lv2Effect/Lv2FxControlDialog.h b/plugins/Lv2Effect/Lv2FxControlDialog.h index f38c0364bfa..d646e0857f2 100644 --- a/plugins/Lv2Effect/Lv2FxControlDialog.h +++ b/plugins/Lv2Effect/Lv2FxControlDialog.h @@ -1,7 +1,7 @@ /* * Lv2FxControlDialog.h - Lv2FxControlDialog implementation * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -26,7 +26,7 @@ #define LV2_FX_CONTROL_DIALOG_H #include "EffectControlDialog.h" -#include "Lv2ViewBase.h" +#include "Lv2ProcView.h" namespace lmms { @@ -36,7 +36,7 @@ class Lv2FxControls; namespace gui { -class Lv2FxControlDialog : public EffectControlDialog, public Lv2ViewBase +class Lv2FxControlDialog : public EffectControlDialog, public Lv2ProcView { Q_OBJECT diff --git a/plugins/Lv2Effect/Lv2FxControls.cpp b/plugins/Lv2Effect/Lv2FxControls.cpp index 72c387ba79b..fedaaccf970 100644 --- a/plugins/Lv2Effect/Lv2FxControls.cpp +++ b/plugins/Lv2Effect/Lv2FxControls.cpp @@ -1,7 +1,7 @@ /* * Lv2FxControls.cpp - Lv2FxControls implementation * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -36,7 +36,7 @@ namespace lmms Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) : EffectControls(effect), - Lv2ControlBase(this, uri) + Lv2Proc(this, uri) { connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged, this, &Lv2FxControls::onSampleRateChanged); @@ -47,7 +47,7 @@ Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) : void Lv2FxControls::reload() { - Lv2ControlBase::reload(); + Lv2Proc::reload(); emit modelChanged(); } @@ -67,7 +67,7 @@ void Lv2FxControls::onSampleRateChanged() void Lv2FxControls::saveSettings(QDomDocument &doc, QDomElement &that) { - Lv2ControlBase::saveSettings(doc, that); + Lv2Proc::saveSettings(doc, that); } @@ -75,7 +75,7 @@ void Lv2FxControls::saveSettings(QDomDocument &doc, QDomElement &that) void Lv2FxControls::loadSettings(const QDomElement &that) { - Lv2ControlBase::loadSettings(that); + Lv2Proc::loadSettings(that); } @@ -83,7 +83,7 @@ void Lv2FxControls::loadSettings(const QDomElement &that) int Lv2FxControls::controlCount() { - return static_cast(Lv2ControlBase::controlCount()); + return static_cast(Lv2Proc::controlCount()); } diff --git a/plugins/Lv2Effect/Lv2FxControls.h b/plugins/Lv2Effect/Lv2FxControls.h index 9f37362929a..a47a82447e2 100644 --- a/plugins/Lv2Effect/Lv2FxControls.h +++ b/plugins/Lv2Effect/Lv2FxControls.h @@ -1,7 +1,7 @@ /* * Lv2FxControls.h - Lv2FxControls implementation * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -26,7 +26,7 @@ #define LV2_FX_CONTROLS_H #include "EffectControls.h" -#include "Lv2ControlBase.h" +#include "Lv2Proc.h" namespace lmms { @@ -40,7 +40,7 @@ class Lv2FxControlDialog; } -class Lv2FxControls : public EffectControls, public Lv2ControlBase +class Lv2FxControls : public EffectControls, public Lv2Proc { Q_OBJECT signals: @@ -53,7 +53,7 @@ class Lv2FxControls : public EffectControls, public Lv2ControlBase void loadSettings(const QDomElement &that) override; inline QString nodeName() const override { - return Lv2ControlBase::nodeName(); + return Lv2Proc::nodeName(); } int controlCount() override; diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 8da2d913ed3..8b91408608b 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -1,7 +1,7 @@ /* * Lv2Instrument.cpp - implementation of LV2 instrument * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -80,7 +80,7 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, Flag::IsSingleStreamed #endif ), - Lv2ControlBase(this, key->attributes["uri"]) + Lv2Proc(this, key->attributes["uri"]) { clearRunningNotes(); @@ -108,7 +108,7 @@ Lv2Instrument::~Lv2Instrument() void Lv2Instrument::reload() { - Lv2ControlBase::reload(); + Lv2Proc::reload(); clearRunningNotes(); emit modelChanged(); } @@ -139,7 +139,7 @@ void Lv2Instrument::onSampleRateChanged() void Lv2Instrument::saveSettings(QDomDocument &doc, QDomElement &that) { - Lv2ControlBase::saveSettings(doc, that); + Lv2Proc::saveSettings(doc, that); } @@ -147,7 +147,7 @@ void Lv2Instrument::saveSettings(QDomDocument &doc, QDomElement &that) void Lv2Instrument::loadSettings(const QDomElement &that) { - Lv2ControlBase::loadSettings(that); + Lv2Proc::loadSettings(that); } @@ -155,7 +155,7 @@ void Lv2Instrument::loadSettings(const QDomElement &that) void Lv2Instrument::loadFile(const QString &file) { - Lv2ControlBase::loadFile(file); + Lv2Proc::loadFile(file); } @@ -187,14 +187,14 @@ void Lv2Instrument::playNote(NotePlayHandle *nph, SampleFrame*) void Lv2Instrument::play(SampleFrame* buf) { - copyModelsFromLmms(); + copyModelsFromCore(); fpp_t fpp = Engine::audioEngine()->framesPerPeriod(); run(fpp); - copyModelsToLmms(); - copyBuffersToLmms(buf, fpp); + copyModelsToCore(); + copyBuffersToCore(buf, fpp); } @@ -219,7 +219,7 @@ void Lv2Instrument::updatePitchRange() QString Lv2Instrument::nodeName() const { - return Lv2ControlBase::nodeName(); + return Lv2Proc::nodeName(); } @@ -235,7 +235,7 @@ namespace gui Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) : InstrumentView(_instrument, _parent), - Lv2ViewBase(this, _instrument) + Lv2ProcView(this, _instrument) { setAutoFillBackground(true); if (m_reloadPluginButton) { @@ -304,7 +304,7 @@ void Lv2InsView::hideEvent(QHideEvent *event) void Lv2InsView::modelChanged() { - Lv2ViewBase::modelChanged(castModel()); + Lv2ProcView::modelChanged(castModel()); connect(castModel(), &Lv2Instrument::modelChanged, this, [this](){ this->modelChanged();} ); } diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 9fbc7b7f699..f9d355d050a 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -1,7 +1,7 @@ /* * Lv2Instrument.h - implementation of LV2 instrument * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -30,8 +30,8 @@ #include "Instrument.h" #include "InstrumentView.h" -#include "Lv2ControlBase.h" -#include "Lv2ViewBase.h" +#include "Lv2ProcView.h" +#include "Lv2Proc.h" #include "Note.h" // whether to use MIDI vs playHandle @@ -48,7 +48,7 @@ class Lv2InsView; } -class Lv2Instrument : public Instrument, public Lv2ControlBase +class Lv2Instrument : public Instrument, public Lv2Proc { Q_OBJECT signals: @@ -73,7 +73,7 @@ class Lv2Instrument : public Instrument, public Lv2ControlBase /* realtime funcs */ - bool hasNoteInput() const override { return Lv2ControlBase::hasNoteInput(); } + bool hasNoteInput() const override { return Lv2Proc::hasNoteInput(); } #ifdef LV2_INSTRUMENT_USE_MIDI bool handleMidiEvent(const MidiEvent &event, const TimePos &time = TimePos(), f_cnt_t offset = 0) override; @@ -106,7 +106,7 @@ namespace gui { -class Lv2InsView : public InstrumentView, public Lv2ViewBase +class Lv2InsView : public InstrumentView, public Lv2ProcView { Q_OBJECT public: diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1e2c4f3cfdb..22b984139ec 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -37,7 +37,6 @@ set(LMMS_SRCS core/LadspaControl.cpp core/LadspaManager.cpp core/LfoController.cpp - core/LinkedModelGroups.cpp core/LocklessAllocator.cpp core/MeterModel.cpp core/Metronome.cpp @@ -46,6 +45,7 @@ set(LMMS_SRCS core/MixHelpers.cpp core/Model.cpp core/ModelVisitor.cpp + core/ModelGroup.cpp core/Note.cpp core/NotePlayHandle.cpp core/Oscillator.cpp @@ -108,7 +108,6 @@ set(LMMS_SRCS core/audio/AudioSdl.cpp core/lv2/Lv2Basics.cpp - core/lv2/Lv2ControlBase.cpp core/lv2/Lv2Evbuf.cpp core/lv2/Lv2Features.cpp core/lv2/Lv2Ports.cpp diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp deleted file mode 100644 index c52bce43310..00000000000 --- a/src/core/LinkedModelGroups.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * LinkedModelGroups.cpp - base classes for groups of linked 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 -#include - -#include "LinkedModelGroups.h" - - -#include "AutomatableModel.h" - - - -namespace lmms -{ - -/* - LinkedModelGroup -*/ - - -void LinkedModelGroup::linkControls(LinkedModelGroup *other) -{ - foreach_model([&other](const std::string& id, ModelInfo& inf) - { - auto itr2 = other->m_models.find(id); - Q_ASSERT(itr2 != other->m_models.end()); - AutomatableModel::linkModels(inf.m_model, itr2->second.m_model); - }); -} - - - - -void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) -{ - foreach_model([&doc, &that](const std::string& , ModelInfo& inf) - { - inf.m_model->saveSettings(doc, that, /*m_models[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ - }); -} - - - - -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, /*m_models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ - }); -} - - - - -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()); - } -} - - - - -bool LinkedModelGroup::eraseModel(const QString& name) -{ - return m_models.erase(name.toStdString()) > 0; -} - - - - -void LinkedModelGroup::clearModels() -{ - m_models.clear(); -} - - - - -bool LinkedModelGroup::containsModel(const QString &name) const -{ - return m_models.find(name.toStdString()) != m_models.end(); -} - - - - -/* - LinkedModelGroups -*/ - - - -void LinkedModelGroups::linkAllModels() -{ - LinkedModelGroup* first = getGroup(0); - for (size_t i = 1; auto cur = getGroup(i); ++i) - { - first->linkControls(cur); - } -} - - - - -void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) -{ - LinkedModelGroup* grp0 = getGroup(0); - if (grp0) - { - QDomElement models = doc.createElement("models"); - that.appendChild(models); - grp0->saveValues(doc, models); - } - else { /* don't even add a "models" node */ } -} - - - - -void LinkedModelGroups::loadSettings(const QDomElement& that) -{ - QDomElement models = that.firstChildElement("models"); - if (auto grp0 = getGroup(0); !models.isNull() && grp0) - { - // only load the first group, the others are linked to the first - grp0->loadValues(models); - } -} - - -} // namespace lmms diff --git a/src/core/ModelGroup.cpp b/src/core/ModelGroup.cpp new file mode 100644 index 00000000000..80a230af2ca --- /dev/null +++ b/src/core/ModelGroup.cpp @@ -0,0 +1,85 @@ +/* + * ModelGroup.cpp - base classes for groups of models + * + * Copyright (c) 2019-2024 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 "ModelGroup.h" + +#include +#include + +#include "AutomatableModel.h" + +namespace lmms { + +/* + ModelGroup +*/ + +void ModelGroup::saveSettings(QDomDocument& doc, QDomElement& that) +{ + QDomElement models = doc.createElement("models"); + that.appendChild(models); + foreach_model([&doc, &models](const std::string&, ModelInfo& inf) { + inf.m_model->saveSettings(doc, models, inf.m_name); /* TODO: m_name useful */ + }); +} + +void ModelGroup::loadSettings(const QDomElement& that) +{ + QDomElement models = that.firstChildElement("models"); + foreach_model([&models](const std::string&, ModelInfo& inf) { + // try to load, if it fails, this will load a sane initial value + inf.m_model->loadSettings(models, inf.m_name); /* TODO: m_name useful */ + }); +} + +void ModelGroup::addModel(AutomatableModel* model, const QString& name) +{ + model->setObjectName(name); + m_models.emplace(std::string(name.toUtf8().data()), ModelInfo(name, model)); + /* + * The following code is currently not used because Models can not be removed + * in no kind of implementation that uses ModelGroup. The code is + * deactivated because this is not a QObject anymore (an ugly connection + * handler would be required for this) + */ +#if 0 + QObject::connect(model, &AutomatableModel::destroyed, + this, [this, model](jo_id_t){ + if (const auto it = m_models.find(model->objectName().toStdString()); it != m_models.end()) + { + m_models.erase(it); + } + }, + Qt::DirectConnection); +#endif + // View needs to create another child view, e.g. a new knob: + emit m_parent->dataChanged(); +} + +void ModelGroup::removeModel(AutomatableModel* mdl) +{ + if (const auto it = m_models.find(mdl->objectName().toStdString()); it != m_models.end()) { m_models.erase(it); } +} + +} // namespace lmms diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp deleted file mode 100644 index 0147ebd6e6b..00000000000 --- a/src/core/lv2/Lv2ControlBase.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Lv2ControlBase.cpp - Lv2 control base class - * - * Copyright (c) 2018-2023 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 -#include -#include - -#include "Engine.h" -#include "Lv2Manager.h" -#include "Lv2Proc.h" - - -namespace lmms -{ - - -Plugin::Type Lv2ControlBase::check(const LilvPlugin *plugin, - std::vector &issues) -{ - // for some reason, all checks can be done by one processor... - return Lv2Proc::check(plugin, issues); -} - - - - -Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : - m_plugin(Engine::getLv2Manager()->getPlugin(uri)) -{ - if (m_plugin) - { - init(that); - } - else - { - qCritical() << "No Lv2 plugin found for URI" << uri; - throw std::runtime_error("No Lv2 plugin found for given URI"); - } -} - - - - -Lv2ControlBase::~Lv2ControlBase() = default; - - - - -void Lv2ControlBase::init(Model* meAsModel) -{ - int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo - while (channelsLeft > 0) - { - std::unique_ptr newOne = std::make_unique(m_plugin, meAsModel); - 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(std::move(newOne)); - } - m_channelsPerProc = DEFAULT_CHANNELS / m_procs.size(); - linkAllModels(); -} - - - - -void Lv2ControlBase::shutdown() -{ - // currently nothing to do here -} - - - - -void Lv2ControlBase::reload() -{ - for (const auto& c : m_procs) { c->reload(); } -} - - - - -LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) -{ - return (m_procs.size() > idx) ? m_procs[idx].get() : nullptr; -} - - - - -const LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx) const -{ - return (m_procs.size() > idx) ? m_procs[idx].get() : nullptr; -} - - - - -void Lv2ControlBase::copyModelsFromLmms() { - for (const auto& c : m_procs) { c->copyModelsFromCore(); } -} - - - - -void Lv2ControlBase::copyModelsToLmms() const -{ - for (const auto& c : m_procs) { c->copyModelsToCore(); } -} - - - - -void Lv2ControlBase::copyBuffersFromLmms(const SampleFrame* buf, fpp_t frames) { - unsigned firstChan = 0; // tell the procs which channels they shall read from - for (const auto& c : m_procs) - { - c->copyBuffersFromCore(buf, firstChan, m_channelsPerProc, frames); - firstChan += m_channelsPerProc; - } -} - - - - -void Lv2ControlBase::copyBuffersToLmms(SampleFrame* buf, fpp_t frames) const { - unsigned firstChan = 0; // tell the procs which channels they shall write to - for (const auto& c : m_procs) { - c->copyBuffersToCore(buf, firstChan, m_channelsPerProc, frames); - firstChan += m_channelsPerProc; - } -} - - - - -void Lv2ControlBase::run(fpp_t frames) { - for (const auto& c : m_procs) { c->run(frames); } -} - - - - -void Lv2ControlBase::saveSettings(QDomDocument &doc, QDomElement &that) -{ - LinkedModelGroups::saveSettings(doc, that); - - // TODO: save state if supported by plugin -} - - - - -void Lv2ControlBase::loadSettings(const QDomElement &that) -{ - LinkedModelGroups::loadSettings(that); - - // TODO: load state if supported by plugin -} - - - - -void Lv2ControlBase::loadFile(const QString &file) -{ - (void)file; -} - - - - -std::size_t Lv2ControlBase::controlCount() const { - std::size_t res = 0; - for (const auto& c : m_procs) { res += c->controlCount(); } - return res; -} - - - - -bool Lv2ControlBase::hasNoteInput() const -{ - return std::any_of(m_procs.begin(), m_procs.end(), - [](const auto& c) { return c->hasNoteInput(); }); -} - - - - -void Lv2ControlBase::handleMidiInputEvent(const MidiEvent &event, - const TimePos &time, f_cnt_t offset) -{ - for (const auto& c : m_procs) { c->handleMidiInputEvent(event, time, offset); } -} - - -} // namespace lmms - - -#endif // LMMS_HAVE_LV2 diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 9807379e44c..57c6276b1ff 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -39,7 +39,7 @@ #include "ConfigManager.h" #include "Engine.h" #include "Plugin.h" -#include "Lv2ControlBase.h" +#include "Lv2Proc.h" #include "Lv2Options.h" #include "PluginIssue.h" @@ -247,7 +247,7 @@ void Lv2Manager::initPlugins() const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); std::vector issues; - Plugin::Type type = Lv2ControlBase::check(curPlug, issues); + Plugin::Type type = Lv2Proc::check(curPlug, issues); std::sort(issues.begin(), issues.end()); auto last = std::unique(issues.begin(), issues.end()); issues.erase(last, issues.end()); diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 7cd9d3a509b..5a9fc643234 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -53,6 +53,11 @@ namespace lmms { + + + + + // container for everything required to store MIDI events going to the plugin struct MidiInputEvent { @@ -191,9 +196,9 @@ class Lv2ProcSuspender : NoCopyNoMove -Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : - LinkedModelGroup(parent), - m_plugin(plugin), +Lv2Proc::Lv2Proc(Model* parent, const QString &uri) : + ModelGroup(parent), + m_plugin(Engine::getLv2Manager()->getPlugin(uri)), m_workLock(1), m_midiInputBuf(m_maxMidiInputEvents), m_midiInputReader(m_midiInputBuf) @@ -325,45 +330,33 @@ void Lv2Proc::copyModelsToCore() -void Lv2Proc::copyBuffersFromCore(const SampleFrame* buf, - unsigned firstChan, unsigned num, - fpp_t frames) +void Lv2Proc::copyBuffersFromCore(const SampleFrame* buf, fpp_t frames) { - inPorts().m_left->copyBuffersFromCore(buf, firstChan, frames); - if (num > 1) + inPorts().m_left->copyBuffersFromCore(buf, 0, frames); + if (inPorts().m_right) { - // 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, firstChan + 1, frames); - } - else - { - inPorts().m_left->averageWithBuffersFromCore(buf, firstChan + 1, frames); - } + inPorts().m_right->copyBuffersFromCore(buf, 1, frames); + } + else + { + // LMMS is always stereo, but the effect can only do mono + // => merge both input buffers together on left port + inPorts().m_left->averageWithBuffersFromCore(buf, 1, frames); } } -void Lv2Proc::copyBuffersToCore(SampleFrame* buf, - unsigned firstChan, unsigned num, - fpp_t frames) const +void Lv2Proc::copyBuffersToCore(SampleFrame* buf, fpp_t frames) const { - outPorts().m_left->copyBuffersToCore(buf, firstChan + 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, firstChan + 1, frames); - } + outPorts().m_left->copyBuffersToCore(buf, 0, frames); + // 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, 1, frames); } diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 4195ec58c1a..bbf20223be3 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -17,10 +17,10 @@ SET(LMMS_SRCS gui/GuiApplication.cpp gui/LadspaControlView.cpp gui/LfoControllerDialog.cpp - gui/LinkedModelGroupViews.cpp + gui/ModelGroupView.cpp gui/LmmsPalette.cpp gui/LmmsStyle.cpp - gui/Lv2ViewBase.cpp + gui/Lv2ProcView.cpp gui/MainApplication.cpp gui/MainWindow.cpp gui/MicrotunerConfig.cpp diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ProcView.cpp similarity index 81% rename from src/gui/Lv2ViewBase.cpp rename to src/gui/Lv2ProcView.cpp index fc025e26856..8aaad5aa941 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ProcView.cpp @@ -1,7 +1,7 @@ /* - * Lv2ViewBase.cpp - base class for Lv2 plugin views + * Lv2ProcView.cpp - base class for Lv2 plugin views * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -22,7 +22,7 @@ * */ -#include "Lv2ViewBase.h" +#include "Lv2ProcView.h" #ifdef LMMS_HAVE_LV2 @@ -40,7 +40,7 @@ #include "embed.h" #include "gui_templates.h" #include "lmms_math.h" -#include "Lv2ControlBase.h" +#include "Lv2Proc.h" #include "Lv2Manager.h" #include "Lv2Proc.h" #include "Lv2Ports.h" @@ -48,13 +48,72 @@ #include "SubWindow.h" +#include "ControlLayout.h" + namespace lmms::gui { -Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : - LinkedModelGroupView (parent, proc, colNum) +Lv2ProcView::Lv2ProcView(QWidget* meAsWidget, Lv2Proc *proc) : + ModelGroupView(meAsWidget, proc), + m_helpWindowEventFilter(this) { + auto grid = new QGridLayout(meAsWidget); + + auto btnBox = new QHBoxLayout(); + 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(adjustedToPixelSize(m_toggleUIButton->font(), 8)); + 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 + AutoLilvNodes props(lilv_plugin_get_value(proc->getPlugin(), + uri(LILV_NS_RDFS "comment").get())); + LILV_FOREACH(nodes, itr, props.get()) + { + const LilvNode* node = lilv_nodes_get(props.get(), itr); + auto infoLabel = new QLabel(QString(lilv_node_as_string(node)).trimmed() + "\n"); + 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 = getGUI()->mainWindow()->addWindowedWidget(infoLabel); + m_helpWindow->setSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Expanding); + m_helpWindow->installEventFilter(&m_helpWindowEventFilter); + m_helpWindow->setAttribute(Qt::WA_DeleteOnClose, false); + m_helpWindow->hide(); + + break; + } + + if (m_reloadPluginButton || m_toggleUIButton || m_helpButton) + { + grid->addLayout(btnBox, Rows::ButtonRow, 0, 1, m_colNum); + } + else { delete btnBox; } + class SetupTheWidget : public Lv2Ports::ConstVisitor { public: @@ -104,19 +163,19 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); proc->foreach_port( - [this, &commentUri](const Lv2Ports::PortBase* port) + [this, &commentUri, &meAsWidget](const Lv2Ports::PortBase* port) { if(!lilv_port_has_property(port->m_plugin, port->m_port, uri(LV2_PORT_PROPS__notOnGUI).get())) { SetupTheWidget setup; - setup.m_parent = this; + setup.m_parent = meAsWidget; setup.m_commentUri = commentUri.get(); port->accept(setup); if (setup.m_control) { - addControl(setup.m_control, + addControl(meAsWidget, setup.m_control, lilv_node_as_string(lilv_port_get_symbol( port->m_plugin, port->m_port)), port->name().toUtf8().data(), @@ -129,81 +188,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : -AutoLilvNode Lv2ViewProc::uri(const char *uriStr) -{ - return Engine::getLv2Manager()->uri(uriStr); -} - - - - -Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) : - m_helpWindowEventFilter(this) -{ - auto grid = new QGridLayout(meAsWidget); - - auto btnBox = new QHBoxLayout(); - 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(adjustedToPixelSize(m_toggleUIButton->font(), 8)); - 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 - 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.get(), itr); - auto infoLabel = new QLabel(QString(lilv_node_as_string(node)).trimmed() + "\n"); - 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 = getGUI()->mainWindow()->addWindowedWidget(infoLabel); - m_helpWindow->setSizePolicy(QSizePolicy::Expanding, - QSizePolicy::Expanding); - m_helpWindow->installEventFilter(&m_helpWindowEventFilter); - m_helpWindow->setAttribute(Qt::WA_DeleteOnClose, false); - m_helpWindow->hide(); - - break; - } - - if (m_reloadPluginButton || m_toggleUIButton || m_helpButton) - { - 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); -} - - - - -Lv2ViewBase::~Lv2ViewBase() { +Lv2ProcView::~Lv2ProcView() { closeHelpWindow(); // TODO: hide UI if required } @@ -211,14 +196,14 @@ Lv2ViewBase::~Lv2ViewBase() { -void Lv2ViewBase::toggleUI() +void Lv2ProcView::toggleUI() { } -void Lv2ViewBase::toggleHelp(bool visible) +void Lv2ProcView::toggleHelp(bool visible) { if (m_helpWindow) { @@ -230,7 +215,7 @@ void Lv2ViewBase::toggleHelp(bool visible) -void Lv2ViewBase::closeHelpWindow() +void Lv2ProcView::closeHelpWindow() { if (m_helpWindow) { m_helpWindow->close(); } } @@ -238,21 +223,21 @@ void Lv2ViewBase::closeHelpWindow() -void Lv2ViewBase::modelChanged(Lv2ControlBase *ctrlBase) +void Lv2ProcView::modelChanged(Lv2Proc *proc) { // reconnect models if (m_toggleUIButton) { - m_toggleUIButton->setChecked(ctrlBase->hasGui()); + m_toggleUIButton->setChecked(proc->hasGui()); } - LinkedModelGroupsView::modelChanged(ctrlBase); + ModelGroupView::modelChanged(proc); } -AutoLilvNode Lv2ViewBase::uri(const char *uriStr) +AutoLilvNode Lv2ProcView::uri(const char *uriStr) { return Engine::getLv2Manager()->uri(uriStr); } @@ -260,7 +245,7 @@ AutoLilvNode Lv2ViewBase::uri(const char *uriStr) -void Lv2ViewBase::onHelpWindowClosed() +void Lv2ProcView::onHelpWindowClosed() { m_helpButton->setChecked(true); } @@ -268,8 +253,8 @@ void Lv2ViewBase::onHelpWindowClosed() -HelpWindowEventFilter::HelpWindowEventFilter(Lv2ViewBase* viewBase) : - m_viewBase(viewBase) {} +HelpWindowEventFilter::HelpWindowEventFilter(Lv2ProcView* viewBase) : + m_procView(viewBase) {} @@ -277,7 +262,7 @@ HelpWindowEventFilter::HelpWindowEventFilter(Lv2ViewBase* viewBase) : bool HelpWindowEventFilter::eventFilter(QObject* , QEvent* event) { if (event->type() == QEvent::Close) { - m_viewBase->m_helpButton->setChecked(false); + m_procView->m_helpButton->setChecked(false); return true; } return false; diff --git a/src/gui/LinkedModelGroupViews.cpp b/src/gui/ModelGroupView.cpp similarity index 56% rename from src/gui/LinkedModelGroupViews.cpp rename to src/gui/ModelGroupView.cpp index f77edcdb9de..db156e35cd6 100644 --- a/src/gui/LinkedModelGroupViews.cpp +++ b/src/gui/ModelGroupView.cpp @@ -1,7 +1,7 @@ /* - * LinkedModelGroupViews.h - view for groups of linkable models + * ModelGroupView.h - view for groups of models * - * Copyright (c) 2019-2019 Johannes Lorenz + * Copyright (c) 2019-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -22,45 +22,31 @@ * */ -#include "LinkedModelGroupViews.h" +#include "ModelGroupView.h" #include -#include "Controls.h" -#include "ControlLayout.h" -#include "LinkedModelGroups.h" - -namespace lmms::gui -{ - -/* - LinkedModelGroupViewBase -*/ +#include "ControlLayout.h" +#include "Controls.h" +#include "ModelGroup.h" +namespace lmms::gui { -LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, - LinkedModelGroup *model, std::size_t colNum) : - QWidget(parent), - m_model(model), - m_colNum(colNum), - m_layout(new ControlLayout(this)) +ModelGroupView::ModelGroupView(QWidget* parent, lmms::ModelGroup* model) + : m_model(model) + , m_layout(new ControlLayout(parent)) { // This is required to remove the focus of the line edit // when e.g. another spin box is being clicked. // Removing the focus is wanted because in many cases, the user wants to // quickly play notes on the virtual keyboard. - setFocusPolicy( Qt::StrongFocus ); + parent->setFocusPolicy(Qt::StrongFocus); // TODO: here? } - - - -void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) +void ModelGroupView::modelChanged(ModelGroup* modelGroup) { // reconnect models - group->foreach_model([this](const std::string& str, - const LinkedModelGroup::ModelInfo& minf) - { + modelGroup->foreach_model([this](const std::string& str, const ModelGroup::ModelInfo& minf) { 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 @@ -69,44 +55,21 @@ void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) // no widget? this can happen when the whole view is being destroyed // (for some strange reasons) } - else - { - itr->second->setModel(minf.m_model); - } + else { itr->second->setModel(minf.m_model); } }); - m_model = group; + m_model = modelGroup; } - - - -void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, - const std::string &display, bool removable) +void ModelGroupView::addControl( + QWidget* parent, Control* ctrl, const std::string& id, const std::string& display, bool removable) { if (ctrl) { - auto box = new QWidget(this); + auto box = new QWidget(parent); auto boxLayout = new QHBoxLayout(box); boxLayout->addWidget(ctrl->topWidget()); - if (removable) - { - auto 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); @@ -115,13 +78,10 @@ void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, m_widgets.emplace(id, std::unique_ptr(ctrl)); } - if (isHidden()) { setHidden(false); } + if (parent->isHidden()) { parent->setHidden(false); } } - - - -void LinkedModelGroupView::removeControl(const QString& key) +void ModelGroupView::removeControl(const QString& key) { auto itr = m_widgets.find(key.toStdString()); if (itr != m_widgets.end()) @@ -142,29 +102,9 @@ void LinkedModelGroupView::removeControl(const QString& key) } } - - - -void LinkedModelGroupView::removeFocusFromSearchBar() +void ModelGroupView::removeFocusFromSearchBar() { m_layout->removeFocusFromSearchBar(); } - -/* - LinkedModelGroupsViewBase -*/ - - -void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) -{ - LinkedModelGroupView* groupView = getGroupView(); - LinkedModelGroup* group0 = groups->getGroup(0); - if (group0 && groupView) - { - groupView->modelChanged(group0); - } -} - - } // namespace lmms::gui