diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index 6b2c7519a..0a4686fb2 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -63,6 +63,7 @@ SET(LMMS_PLUGIN_LIST StereoEnhancer StereoMatrix Stk + TapTempo VstBase Vestige VstEffect diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index d016fcf0b..31b4f0bd5 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -40,7 +40,7 @@ class Track; class AudioPort; -class SamplePlayHandle : public PlayHandle +class LMMS_EXPORT SamplePlayHandle : public PlayHandle { public: SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort = true ); diff --git a/plugins/TapTempo/CMakeLists.txt b/plugins/TapTempo/CMakeLists.txt new file mode 100644 index 000000000..1fb6ba8dc --- /dev/null +++ b/plugins/TapTempo/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(taptempo TapTempo.cpp TapTempoView.cpp MOCFILES TapTempo.h TapTempoView.h EMBEDDED_RESOURCES logo.png) \ No newline at end of file diff --git a/plugins/TapTempo/TapTempo.cpp b/plugins/TapTempo/TapTempo.cpp new file mode 100644 index 000000000..aad8d99b9 --- /dev/null +++ b/plugins/TapTempo/TapTempo.cpp @@ -0,0 +1,85 @@ +/* + * TapTempo.cpp - Plugin to count beats per minute + * + * + * Copyright (c) 2022 saker + * + * + * 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 "TapTempo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AudioEngine.h" +#include "Engine.h" +#include "LedCheckBox.h" +#include "SamplePlayHandle.h" +#include "Song.h" +#include "embed.h" +#include "plugin_export.h" + +namespace lmms { + +extern "C" { +Plugin::Descriptor PLUGIN_EXPORT taptempo_plugin_descriptor + = {LMMS_STRINGIFY(PLUGIN_NAME), "Tap Tempo", QT_TRANSLATE_NOOP("PluginBrowser", "Tap to the beat"), + "saker ", 0x0100, Plugin::Type::Tool, new PluginPixmapLoader("logo"), nullptr, nullptr}; + +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void*) +{ + return new TapTempo; +} +} + +TapTempo::TapTempo() + : ToolPlugin(&taptempo_plugin_descriptor, nullptr) +{ +} + +void TapTempo::onBpmClick() +{ + const auto currentTime = clock::now(); + if (m_numTaps == 0) + { + m_startTime = currentTime; + } + else + { + using namespace std::chrono_literals; + const auto secondsElapsed = (currentTime - m_startTime) / 1.0s; + if (m_numTaps >= m_tapsNeededToDisplay) { m_bpm = m_numTaps / secondsElapsed * 60; } + } + + ++m_numTaps; +} + +QString TapTempo::nodeName() const +{ + return taptempo_plugin_descriptor.name; +} +} // namespace lmms \ No newline at end of file diff --git a/plugins/TapTempo/TapTempo.h b/plugins/TapTempo/TapTempo.h new file mode 100644 index 000000000..be540b8da --- /dev/null +++ b/plugins/TapTempo/TapTempo.h @@ -0,0 +1,61 @@ +/* + * TapTempo.h - Plugin to count beats per minute + * + * Copyright (c) 2022 saker + * + * 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_TAP_TEMPO_H +#define LMMS_TAP_TEMPO_H + +#include + +#include "TapTempoView.h" +#include "ToolPlugin.h" + +namespace lmms { + +class TapTempo : public ToolPlugin +{ + Q_OBJECT +public: + using clock = std::chrono::steady_clock; + + TapTempo(); + void onBpmClick(); + + QString nodeName() const override; + void saveSettings(QDomDocument&, QDomElement&) override {} + void loadSettings(const QDomElement&) override {} + + gui::PluginView* instantiateView(QWidget*) override { return new gui::TapTempoView(this); } + +private: + std::chrono::time_point m_startTime; + int m_numTaps = 0; + int m_tapsNeededToDisplay = 2; + double m_bpm = 0.0; + bool m_showDecimal = false; + + friend class gui::TapTempoView; +}; +} // namespace lmms + +#endif // LMMS_TAP_TEMPO_H diff --git a/plugins/TapTempo/TapTempoView.cpp b/plugins/TapTempo/TapTempoView.cpp new file mode 100644 index 000000000..d6c24fcf5 --- /dev/null +++ b/plugins/TapTempo/TapTempoView.cpp @@ -0,0 +1,168 @@ +/* + * TapTempoView.cpp - Plugin to count beats per minute + * + * + * Copyright (c) 2022 saker + * + * + * 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 "TapTempoView.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Engine.h" +#include "SamplePlayHandle.h" +#include "Song.h" +#include "TapTempo.h" + +namespace lmms::gui { +TapTempoView::TapTempoView(TapTempo* plugin) + : ToolPluginView(plugin) + , m_plugin(plugin) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + auto font = QFont(); + font.setPointSize(24); + + m_tapButton = new QPushButton(); + m_tapButton->setFixedSize(200, 200); + m_tapButton->setFont(font); + m_tapButton->setText(tr("0")); + + auto precisionCheckBox = new QCheckBox(tr("Precision")); + precisionCheckBox->setFocusPolicy(Qt::NoFocus); + precisionCheckBox->setToolTip(tr("Display in high precision")); + precisionCheckBox->setText(tr("Precision")); + + auto muteCheckBox = new QCheckBox(tr("0.0 ms")); + muteCheckBox->setFocusPolicy(Qt::NoFocus); + muteCheckBox->setToolTip(tr("Mute metronome")); + muteCheckBox->setText(tr("Mute")); + + m_msLabel = new QLabel(); + m_msLabel->setFocusPolicy(Qt::NoFocus); + m_msLabel->setToolTip(tr("BPM in milliseconds")); + m_msLabel->setText(tr("0 ms")); + + m_hzLabel = new QLabel(); + m_hzLabel->setFocusPolicy(Qt::NoFocus); + m_hzLabel->setToolTip(tr("Frequency of BPM")); + m_hzLabel->setText(tr("0.0000 hz")); + + auto resetButton = new QPushButton(tr("Reset")); + resetButton->setFocusPolicy(Qt::NoFocus); + resetButton->setToolTip(tr("Reset counter and sidebar information")); + + auto syncButton = new QPushButton(tr("Sync")); + syncButton->setFocusPolicy(Qt::NoFocus); + syncButton->setToolTip(tr("Sync with project tempo")); + + auto optionLayout = new QVBoxLayout(); + optionLayout->addWidget(precisionCheckBox); + optionLayout->addWidget(muteCheckBox); + + auto bpmInfoLayout = new QVBoxLayout(); + bpmInfoLayout->addWidget(m_msLabel, 0, Qt::AlignHCenter); + bpmInfoLayout->addWidget(m_hzLabel, 0, Qt::AlignHCenter); + + auto sidebarLayout = new QHBoxLayout(); + sidebarLayout->addLayout(optionLayout); + sidebarLayout->addLayout(bpmInfoLayout); + + auto buttonsLayout = new QHBoxLayout(); + buttonsLayout->addWidget(resetButton, 0, Qt::AlignCenter); + buttonsLayout->addWidget(syncButton, 0, Qt::AlignCenter); + + auto mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(m_tapButton, 0, Qt::AlignCenter); + mainLayout->addLayout(buttonsLayout); + mainLayout->addLayout(sidebarLayout); + + connect(m_tapButton, &QPushButton::pressed, this, [this, muteCheckBox]() { + if (!muteCheckBox->isChecked()) + { + const auto timeSigNumerator = Engine::getSong()->getTimeSigModel().getNumerator(); + Engine::audioEngine()->addPlayHandle(new SamplePlayHandle( + m_plugin->m_numTaps % timeSigNumerator == 0 ? "misc/metronome02.ogg" : "misc/metronome01.ogg")); + } + + m_plugin->onBpmClick(); + updateLabels(); + }); + + connect(resetButton, &QPushButton::pressed, this, [this]() { closeEvent(nullptr); }); + + connect(precisionCheckBox, &QCheckBox::toggled, [this](bool checked) { + m_plugin->m_showDecimal = checked; + updateLabels(); + }); + + connect(syncButton, &QPushButton::clicked, this, [this]() { + const auto& tempoModel = Engine::getSong()->tempoModel(); + if (m_plugin->m_bpm < tempoModel.minValue() || m_plugin->m_bpm > tempoModel.maxValue()) { return; } + Engine::getSong()->setTempo(std::round(m_plugin->m_bpm)); + }); + + hide(); + if (parentWidget()) + { + parentWidget()->hide(); + parentWidget()->layout()->setSizeConstraint(QLayout::SetFixedSize); + + Qt::WindowFlags flags = parentWidget()->windowFlags(); + flags |= Qt::MSWindowsFixedSizeDialogHint; + flags &= ~Qt::WindowMaximizeButtonHint; + parentWidget()->setWindowFlags(flags); + } +} + +void TapTempoView::updateLabels() +{ + const double bpm = m_plugin->m_showDecimal ? m_plugin->m_bpm : std::round(m_plugin->m_bpm); + const double hz = bpm / 60; + const double ms = bpm > 0 ? 1 / hz * 1000 : 0; + + m_tapButton->setText(QString::number(bpm, 'f', m_plugin->m_showDecimal ? 1 : 0)); + m_msLabel->setText(tr("%1 ms").arg(ms, 0, 'f', m_plugin->m_showDecimal ? 1 : 0)); + m_hzLabel->setText(tr("%1 hz").arg(hz, 0, 'f', 4)); +} + +void TapTempoView::keyPressEvent(QKeyEvent* event) +{ + QWidget::keyPressEvent(event); + if (!event->isAutoRepeat()) { m_plugin->onBpmClick(); } +} + +void TapTempoView::closeEvent(QCloseEvent*) +{ + m_plugin->m_numTaps = 0; + m_plugin->m_bpm = 0; + updateLabels(); +} + +} // namespace lmms::gui diff --git a/plugins/TapTempo/TapTempoView.h b/plugins/TapTempo/TapTempoView.h new file mode 100644 index 000000000..a2a522d3f --- /dev/null +++ b/plugins/TapTempo/TapTempoView.h @@ -0,0 +1,64 @@ +/* + * TapTempoView.h - Plugin to count beats per minute + * + * + * Copyright (c) 2022 saker + * + * + * 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_TAP_TEMPO_VIEW_H +#define LMMS_GUI_TAP_TEMPO_VIEW_H + +#include "ToolPluginView.h" + +class QVBoxLayout; +class QHBoxLayout; +class QPushButton; +class QLabel; +class QCloseEvent; +class QKeyEvent; +class QCheckBox; + +namespace lmms { +class TapTempo; +} + +namespace lmms::gui { + +class TapTempoView : public ToolPluginView +{ + Q_OBJECT +public: + TapTempoView(TapTempo* plugin); + void updateLabels(); + void keyPressEvent(QKeyEvent* event) override; + void closeEvent(QCloseEvent*) override; + +private: + QPushButton* m_tapButton; + QLabel* m_msLabel; + QLabel* m_hzLabel; + TapTempo* m_plugin; + friend class TapTempo; +}; +} // namespace lmms::gui + +#endif // LMMS_GUI_TAP_TEMPO_VIEW_H diff --git a/plugins/TapTempo/logo.png b/plugins/TapTempo/logo.png new file mode 100644 index 000000000..67e6a3503 Binary files /dev/null and b/plugins/TapTempo/logo.png differ