diff --git a/plugins/TemplateEffect/CMakeLists.txt b/plugins/TemplateEffect/CMakeLists.txt new file mode 100644 index 00000000000..bff8268da39 --- /dev/null +++ b/plugins/TemplateEffect/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(templateeffect TemplateEffect.cpp TemplateEffectControls.cpp TemplateEffectControlDialog.cpp MOCFILES TemplateEffectControls.h TemplateEffectControlDialog.h EMBEDDED_RESOURCES artwork.svg logo.svg) diff --git a/plugins/TemplateEffect/TemplateEffect.cpp b/plugins/TemplateEffect/TemplateEffect.cpp new file mode 100644 index 00000000000..20a1a5d9899 --- /dev/null +++ b/plugins/TemplateEffect/TemplateEffect.cpp @@ -0,0 +1,79 @@ +/* + * TemplateEffect.cpp - Example effect boilerplate code + * + * Copyright (c) 2014 Vesa Kivimäki + * 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 "TemplateEffect.h" + +#include "embed.h" +#include "plugin_export.h" + +namespace lmms +{ + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT templateeffect_plugin_descriptor = +{ + LMMS_STRINGIFY(PLUGIN_NAME), + "TemplateHumanName", + QT_TRANSLATE_NOOP("PluginBrowser", "TemplateDescription"), + "TemplateAuthor", + 0x0100, + Plugin::Type::Effect, + new PluginPixmapLoader("logo"), + nullptr, + nullptr, +} ; + +} + + +TemplateEffectEffect::TemplateEffectEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) : + Effect(&templateeffect_plugin_descriptor, parent, key), + m_controls(this) +{ +} + + +Effect::ProcessStatus TemplateEffectEffect::processImpl(SampleFrame* buffer, const fpp_t frames) +{ + // Put your audio processing code here + + return ProcessStatus::ContinueIfNotQuiet; +} + + +extern "C" +{ + +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* parent, void* data) +{ + return new TemplateEffectEffect(parent, static_cast(data)); +} + +} + +} // namespace lmms diff --git a/plugins/TemplateEffect/TemplateEffect.h b/plugins/TemplateEffect/TemplateEffect.h new file mode 100644 index 00000000000..4df55c29fe5 --- /dev/null +++ b/plugins/TemplateEffect/TemplateEffect.h @@ -0,0 +1,56 @@ +/* + * TemplateEffect.h - Example effect boilerplate code + * + * Copyright (c) 2014 Vesa Kivimäki + * 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. + * + */ + +#ifndef LMMS_TEMPLATE_EFFECT_H +#define LMMS_TEMPLATE_EFFECT_H + +#include "Effect.h" +#include "TemplateEffectControls.h" + +namespace lmms +{ + +class TemplateEffectEffect : public Effect +{ +public: + TemplateEffectEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key); + ~TemplateEffectEffect() override = default; + + ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override; + + EffectControls* controls() override + { + return &m_controls; + } + +private: + TemplateEffectControls m_controls; + + friend class TemplateEffectControls; +}; + +} // namespace lmms + +#endif // LMMS_TEMPLATE_EFFECT_H diff --git a/plugins/TemplateEffect/TemplateEffectControlDialog.cpp b/plugins/TemplateEffect/TemplateEffectControlDialog.cpp new file mode 100644 index 00000000000..daf1fbe7d4c --- /dev/null +++ b/plugins/TemplateEffect/TemplateEffectControlDialog.cpp @@ -0,0 +1,64 @@ +/* + * TemplateEffectControlDialog.cpp - Example effect gui boilerplate code + * + * Copyright (c) 2014 Vesa Kivimäki + * 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 "TemplateEffectControlDialog.h" +#include "TemplateEffectControls.h" +#include "embed.h" +#include "Knob.h" +#include "Fader.h" +#include "LedCheckBox.h" +#include "LcdSpinBox.h" + +#include + +namespace lmms::gui +{ + +TemplateEffectControlDialog::TemplateEffectControlDialog(TemplateEffectControls* controls) : + EffectControlDialog(controls) +{ + setAutoFillBackground(true); + /* Uncomment this if you want to use a background image + QPalette pal; + pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); + setPalette(pal); + setFixedSize(100, 110); + */ + + QHBoxLayout* mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(20,20,20,20); + + TEMPLATE_KNOB_LOOP_START + Knob* knobTEMPLATE_KNOB_NUMBER = new Knob(KnobType::Bright26, this); + knobTEMPLATE_KNOB_NUMBER->setModel(&controls->m_modelTEMPLATE_KNOB_NUMBER); + knobTEMPLATE_KNOB_NUMBER->setLabel(tr("Knob TEMPLATE_KNOB_NUMBER")); + knobTEMPLATE_KNOB_NUMBER->setHintText(tr("Value description:"), " units"); + mainLayout->addWidget(knobTEMPLATE_KNOB_NUMBER); + + TEMPLATE_KNOB_LOOP_END + +} + +} // namespace lmms::gui diff --git a/plugins/TemplateEffect/TemplateEffectControlDialog.h b/plugins/TemplateEffect/TemplateEffectControlDialog.h new file mode 100644 index 00000000000..afc0c19be34 --- /dev/null +++ b/plugins/TemplateEffect/TemplateEffectControlDialog.h @@ -0,0 +1,56 @@ +/* + * TemplateEffectControlDialog.h - Example effect gui boilerplate code + * + * Copyright (c) 2014 Vesa Kivimäki + * 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. + * + */ + +#ifndef LMMS_GUI_TEMPLATE_EFFECT_CONTROL_DIALOG_H +#define LMMS_GUI_TEMPLATE_EFFECT_CONTROL_DIALOG_H + +#include "EffectControlDialog.h" + +namespace lmms +{ + +class TemplateEffectControls; +class FloatModel; + +namespace gui +{ + +class Knob; + +class TemplateEffectControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + TemplateEffectControlDialog(TemplateEffectControls* controls); + ~TemplateEffectControlDialog() override = default; + + bool isResizable() const override { return true; } +}; + +} // namespace gui + +} // namespace lmms + +#endif // LMMS_GUI_TEMPLATE_EFFECT_CONTROL_DIALOG_H diff --git a/plugins/TemplateEffect/TemplateEffectControls.cpp b/plugins/TemplateEffect/TemplateEffectControls.cpp new file mode 100644 index 00000000000..5fddeb82cc6 --- /dev/null +++ b/plugins/TemplateEffect/TemplateEffectControls.cpp @@ -0,0 +1,60 @@ +/* + * TemplateEffectControls.cpp - Example effect control boilerplate code + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-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 + +#include "TemplateEffectControls.h" +#include "TemplateEffect.h" + +namespace lmms +{ + +TemplateEffectControls::TemplateEffectControls(TemplateEffectEffect* effect) : + EffectControls(effect), + TEMPLATE_KNOB_LOOP_START + m_modelTEMPLATE_KNOB_NUMBER(0.5f, 0.0f, 1.0f, 0.00001f, this, tr("Knob TEMPLATE_KNOB_NUMBER")), + TEMPLATE_KNOB_LOOP_END + m_effect(effect) +{ +} + + +void TemplateEffectControls::loadSettings(const QDomElement& parent) +{ + TEMPLATE_KNOB_LOOP_START + m_modelTEMPLATE_KNOB_NUMBER.loadSettings(parent, "modelTEMPLATE_KNOB_NUMBER"); + TEMPLATE_KNOB_LOOP_END +} + + +void TemplateEffectControls::saveSettings(QDomDocument& doc, QDomElement& parent) +{ + TEMPLATE_KNOB_LOOP_START + m_modelTEMPLATE_KNOB_NUMBER.saveSettings(doc, parent, "modelTEMPLATE_KNOB_NUMBER"); + TEMPLATE_KNOB_LOOP_END +} + + +} // namespace lmms diff --git a/plugins/TemplateEffect/TemplateEffectControls.h b/plugins/TemplateEffect/TemplateEffectControls.h new file mode 100644 index 00000000000..5953a9b3f86 --- /dev/null +++ b/plugins/TemplateEffect/TemplateEffectControls.h @@ -0,0 +1,74 @@ +/* + * TemplateEffectControls.h - Example effect control boilerplate code + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2008-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. + * + */ + +#ifndef LMMS_TEMPLATE_EFFECT_CONTROLS_H +#define LMMS_TEMPLATE_EFFECT_CONTROLS_H + +#include "EffectControls.h" +#include "TemplateEffectControlDialog.h" + +namespace lmms +{ + +class TemplateEffectEffect; + +namespace gui +{ +class TemplateEffectControlDialog; +} + +class TemplateEffectControls : public EffectControls +{ + Q_OBJECT +public: + TemplateEffectControls(TemplateEffectEffect* effect); + ~TemplateEffectControls() override = default; + + void saveSettings(QDomDocument& doc, QDomElement& parent) override; + void loadSettings(const QDomElement& parent) override; + inline QString nodeName() const override + { + return "TemplateEffectControls"; + } + gui::EffectControlDialog* createView() override + { + return new gui::TemplateEffectControlDialog(this); + } + int controlCount() override { return TEMPLATE_NUM_KNOBS; } + +private: + TEMPLATE_KNOB_LOOP_START + FloatModel m_modelTEMPLATE_KNOB_NUMBER; + TEMPLATE_KNOB_LOOP_END + + TemplateEffectEffect* m_effect; + + friend class gui::TemplateEffectControlDialog; + friend class TemplateEffectEffect; +}; + +} // namespace lmms + +#endif // LMMS_TEMPLATE_EFFECT_CONTROLS_H diff --git a/plugins/TemplateEffect/artwork.svg b/plugins/TemplateEffect/artwork.svg new file mode 100644 index 00000000000..df574cfda90 --- /dev/null +++ b/plugins/TemplateEffect/artwork.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/plugins/TemplateEffect/logo.svg b/plugins/TemplateEffect/logo.svg new file mode 100644 index 00000000000..7920fa6b017 --- /dev/null +++ b/plugins/TemplateEffect/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/setupeffect.py b/plugins/setupeffect.py new file mode 100644 index 00000000000..2e7b869cae3 --- /dev/null +++ b/plugins/setupeffect.py @@ -0,0 +1,171 @@ +# +# setupeffect.py - Script for quickly generating template effect source code +# +# Copyright (c) 2025 regulus79 +# +# 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. +# + + +import os +import shutil + + +# +# Load template files +# + +templateFolderPath = os.path.join(os.path.dirname(__file__), "TemplateEffect") +templateName = "TemplateEffect" +templateNameLowercase = "templateeffect" +templateNameUppercase = "TEMPLATE_EFFECT" +templateHumanName = "TemplateHumanName" +templateDescription = "TemplateDescription" +templateAuthor = "TemplateAuthor" +templateNumKnobs = "TEMPLATE_NUM_KNOBS" + +with open(os.path.join(templateFolderPath, "TemplateEffect.cpp"), "r") as file: + templateMainCpp = file.read() +with open(os.path.join(templateFolderPath, "TemplateEffect.h"), "r") as file: + templateMainHeader = file.read() +with open(os.path.join(templateFolderPath, "TemplateEffectControls.cpp"), "r") as file: + templateControlsCpp = file.read() +with open(os.path.join(templateFolderPath, "TemplateEffectControls.h"), "r") as file: + templateControlsHeader = file.read() +with open(os.path.join(templateFolderPath, "TemplateEffectControlDialog.cpp"), "r") as file: + templateControlDialogCpp = file.read() +with open(os.path.join(templateFolderPath, "TemplateEffectControlDialog.h"), "r") as file: + templateControlsDialogHeader = file.read() +with open(os.path.join(templateFolderPath, "CMakeLists.txt"), "r") as file: + templateCMakeLists = file.read() + + +# +# Get user input +# + +effectName = input("Effect Name (UpperCamelCase): ") + +assert not " " in effectName, "Effect name cannot contain spaces." +assert not effectName[0].isdigit(), "Effect name cannot start with a number." +assert all(c.isalnum() for c in effectName), "Effect name must be alphanumeric." +assert any(c.islower() for c in effectName), "Effect name must be in UpperCamelCase (have at least one lowercase letter.)" +assert any(c.isupper() for c in effectName), "Effect name must be in UpperCamelCase (have at least one uppercase letter.)" + +effectNameLowercase = effectName.lower() +# The uppercase name should have _ between words, so RandomEffect becomes RANDOM_EFFECT +effectNameUppercase = "" +for i in range(len(effectName)): + if i < len(effectName) - 1 and effectName[i + 1].isupper() and effectName[i].islower(): + effectNameUppercase += effectName[i].upper() + "_" + else: + effectNameUppercase += effectName[i].upper() + +print(f"Lowercase version: {effectNameLowercase}, Uppercase version: {effectNameUppercase}") + +effectHumanName = input("Effect Name (Human readable, spaces allowed): ") +effectDescription = input("Effect Description: ") +effectAuthor = input("Effect Author: ") +effectKnobs = int(input("Number of knobs: ")) + + +# +# Create directory and write files +# + +effectFolderPath = os.path.join(os.path.dirname(__file__), effectName) +assert not os.path.exists(effectFolderPath), f"Directory {effectFolderPath} already exists." + +print(f"Creating directory {effectFolderPath}") +os.makedirs(effectFolderPath) + + +def replaceTemplateKeywords(text): + # Replace keywords + text = text\ + .replace(templateName, effectName)\ + .replace(templateNameLowercase, effectNameLowercase)\ + .replace(templateNameUppercase, templateNameUppercase)\ + .replace(templateHumanName, effectHumanName)\ + .replace(templateDescription, effectDescription)\ + .replace(templateAuthor, effectAuthor)\ + .replace(templateNumKnobs, str(effectKnobs)) + # Replace/repeat knob creation code + textLines = text.split("\n") + newTextLines = [] + tmp = [] + inLoop = False + for i, line in enumerate(textLines): + if "TEMPLATE_KNOB_LOOP_START" in line: + inLoop = True + elif "TEMPLATE_KNOB_LOOP_END" in line: + inLoop = False + for knob in range(effectKnobs): + newTextLines.append("\n".join(tmp).replace("TEMPLATE_KNOB_NUMBER", str(knob))) + tmp = [] + elif inLoop: + tmp.append(line) + else: + newTextLines.append(line) + return "\n".join(newTextLines) + + +with open(os.path.join(effectFolderPath, f"{effectName}.cpp"), "w") as file: + file.write(replaceTemplateKeywords(templateMainCpp)) +with open(os.path.join(effectFolderPath, f"{effectName}.h"), "w") as file: + file.write(replaceTemplateKeywords(templateMainHeader)) +with open(os.path.join(effectFolderPath, f"{effectName}Controls.cpp"), "w") as file: + file.write(replaceTemplateKeywords(templateControlsCpp)) +with open(os.path.join(effectFolderPath, f"{effectName}Controls.h"), "w") as file: + file.write(replaceTemplateKeywords(templateControlsHeader)) +with open(os.path.join(effectFolderPath, f"{effectName}ControlDialog.cpp"), "w") as file: + file.write(replaceTemplateKeywords(templateControlDialogCpp)) +with open(os.path.join(effectFolderPath, f"{effectName}ControlDialog.h"), "w") as file: + file.write(replaceTemplateKeywords(templateControlsDialogHeader)) +with open(os.path.join(effectFolderPath, f"CMakeLists.txt"), "w") as file: + file.write(replaceTemplateKeywords(templateCMakeLists)) +shutil.copy(os.path.join(templateFolderPath, "artwork.svg"), os.path.join(effectFolderPath, "artwork.svg")) +shutil.copy(os.path.join(templateFolderPath, "logo.svg"), os.path.join(effectFolderPath, "logo.svg")) + +print(f"{effectName} template files successfully written to {effectFolderPath}") + + +# +# Update ../cmake/modules/PluginList.cmake +# + +with open(os.path.join(os.path.dirname(__file__), f"../cmake/modules/PluginList.cmake"), "r+") as file: + contents = file.readlines() + file.seek(0) + foundList = False + insertedEffectName = False + for i, line in enumerate(contents): + # Find where the list starts + if line == "SET(LMMS_PLUGIN_LIST\n": + foundList = True + elif foundList: + # Then go through the list until the effect with a name after in alphabetical order is found + if line.lstrip() > effectName: + contents.insert(i, f" {effectName}\n") + insertedEffectName = True + break + if insertedEffectName: + file.write("".join(contents)) + print("Updated ../cmake/modules/PluginList.cmake") + else: + print("ERROR Failed to insert effect in ../cmake/modules/PluginList.cmake")