Add bar controller with tempo sync option

Add `TempoSyncBarModelEditor` which adds a tempo sync option to a `BarModelEditor`. Please note that the code was mostly copied and adjusted from the `TempoSyncKnob` class. It was not attempted yet to unify some of the code because with the current design it seems to be a road to "inheritance hell". A better solution might be to refactor the code so that it is more composable.

Another option might be to move the tempo sync functionality into a class like `FloatModelEditorBase`. Although this would not be a 100% right place either because `TempoSyncKnobModel` inherits from `FloatModel`. In that case we'd have specialized code in a generic base class.

Adjust `LadspaWidgetFactory` so that it now returns an instance of a `TempoSyncBarModelEditor` instead of a `TempoSyncKnob`.

Remove the extra layout code from `LadspaMatrixControlDialog.cpp` as most things are bar editors now.

Adjust `TempoSyncKnobModel` so we do not have to make `TempoSyncBarModelEditor` a friend of it.
This commit is contained in:
Michael Gregorius
2023-09-07 17:02:28 +02:00
parent add834e22f
commit b79a8dcbad
6 changed files with 410 additions and 18 deletions

View File

@@ -0,0 +1,88 @@
/*
* TempoSyncBarModelEditor.h - adds bpm to ms conversion for the bar editor class
*
* Copyright (c) 2005-2008 Danny McRae <khjklujn/at/yahoo.com>
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023 Michael Gregorius
*
* 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_TEMPO_SYNC_BAR_MODEL_EDITOR_H
#define LMMS_GUI_TEMPO_SYNC_BAR_MODEL_EDITOR_H
#include <QPixmap>
#include <QPointer>
#include "BarModelEditor.h"
#include "TempoSyncKnobModel.h"
namespace lmms::gui
{
class MeterDialog;
class LMMS_EXPORT TempoSyncBarModelEditor : public BarModelEditor
{
Q_OBJECT
public:
TempoSyncBarModelEditor(QString text, FloatModel * floatModel, QWidget * parent = nullptr);
~TempoSyncBarModelEditor() override;
const QString & syncDescription();
void setSyncDescription( const QString & _new_description );
const QPixmap & syncIcon();
void setSyncIcon( const QPixmap & _new_pix );
TempoSyncKnobModel * model()
{
return castModel<TempoSyncKnobModel>();
}
void modelChanged() override;
signals:
void syncDescriptionChanged( const QString & _new_description );
void syncIconChanged();
protected:
void contextMenuEvent( QContextMenuEvent * _me ) override;
protected slots:
void updateDescAndIcon();
void showCustom();
private:
QPixmap m_tempoSyncIcon;
QString m_tempoSyncDescription;
QPointer<MeterDialog> m_custom;
} ;
} // namespace lmms::gui
#endif // LMMS_GUI_TEMPO_SYNC_BAR_MODEL_EDITOR_H

View File

@@ -82,6 +82,9 @@ public:
void setScale( float _new_scale );
MeterModel & getCustomMeterModel() { return m_custom; }
MeterModel const & getCustomMeterModel() const { return m_custom; }
signals:
void syncModeChanged( lmms::TempoSyncKnobModel::SyncMode _new_mode );
void scaleChanged( float _new_scale );

View File

@@ -166,11 +166,7 @@ void LadspaMatrixControlDialog::arrangeControls(QWidget * parent, QGridLayout* g
QWidget * controlWidget = LadspaWidgetFactory::createWidget(ladspaControl, this);
if (controlWidget)
{
// Align time based controls, i.e. knobs, in the center.
// This defeats the purpose of the widget factory a bit but it makes the design look nicer.
auto alignment = ladspaControl->port()->data_type == BufferDataType::Time ? Qt::AlignCenter : Qt::Alignment();
gridLayout->addWidget(controlWidget, currentRow, currentChannelColumn, alignment);
gridLayout->addWidget(controlWidget, currentRow, currentChannelColumn);
}
// Record the maximum row so that we add a vertical spacer after that row

View File

@@ -34,7 +34,7 @@
// TODO Only for testing! Remove!
#include "FloatModelEditorBase.h"
#include "LedCheckBox.h"
#include "TempoSyncKnob.h"
#include "TempoSyncBarModelEditor.h"
#include <QLabel>
@@ -44,8 +44,6 @@ namespace lmms::gui
QWidget * LadspaWidgetFactory::createWidget(LadspaControl * ladspaControl, QWidget * parent)
{
Knob * knob = nullptr;
auto const * port = ladspaControl->port();
QString const name = port->name;
@@ -66,21 +64,12 @@ QWidget * LadspaWidgetFactory::createWidget(LadspaControl * ladspaControl, QWidg
return new BarModelEditor(name, ladspaControl->knobModel(), parent);
case BufferDataType::Time:
knob = new TempoSyncKnob(KnobType::Bright26, parent, name);
knob->setModel(ladspaControl->tempoSyncKnobModel());
knob->setLabel(name);
break;
return new TempoSyncBarModelEditor(name, ladspaControl->tempoSyncKnobModel(), parent);
default:
return new QLabel(QObject::tr("%1 (unsupported)").arg(name), parent);
}
if (knob != nullptr)
{
knob->setHintText(QObject::tr("Value:"), "");
return knob;
}
return nullptr;
}

View File

@@ -117,6 +117,7 @@ SET(LMMS_SRCS
gui/widgets/SimpleTextFloat.cpp
gui/widgets/TabBar.cpp
gui/widgets/TabWidget.cpp
gui/widgets/TempoSyncBarModelEditor.cpp
gui/widgets/TempoSyncKnob.cpp
gui/widgets/TextFloat.cpp
gui/widgets/TimeDisplayWidget.cpp

View File

@@ -0,0 +1,315 @@
/*
* TempoSyncBarModelEditor.cpp - adds bpm to ms conversion for the bar editor class
*
* Copyright (c) 2005-2007 Danny McRae <khjklujn/at/yahoo.com>
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023 Michael Gregorius
*
* 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 <QMdiArea>
#include "TempoSyncBarModelEditor.h"
#include "Engine.h"
#include "CaptionMenu.h"
#include "embed.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "MeterDialog.h"
#include "Song.h"
#include "SubWindow.h"
namespace lmms::gui
{
TempoSyncBarModelEditor::TempoSyncBarModelEditor(QString text, FloatModel * floatModel, QWidget * parent) :
BarModelEditor(text, floatModel, parent ),
m_tempoSyncIcon( embed::getIconPixmap( "tempo_sync" ) ),
m_tempoSyncDescription( tr( "Tempo Sync" ) ),
m_custom( nullptr )
{
modelChanged();
}
TempoSyncBarModelEditor::~TempoSyncBarModelEditor()
{
if( m_custom )
{
delete m_custom->parentWidget();
}
}
void TempoSyncBarModelEditor::modelChanged()
{
if( model() == nullptr )
{
qWarning( "no TempoSyncKnobModel has been set!" );
}
if( m_custom != nullptr )
{
m_custom->setModel( &model()->getCustomMeterModel() );
}
connect(model(), &TempoSyncKnobModel::syncModeChanged, this, &TempoSyncBarModelEditor::updateDescAndIcon);
connect( this, SIGNAL(sliderMoved(float)),
model(), SLOT(disableSync()));
updateDescAndIcon();
}
void TempoSyncBarModelEditor::contextMenuEvent( QContextMenuEvent * )
{
mouseReleaseEvent( nullptr );
CaptionMenu contextMenu( model()->displayName(), this );
addDefaultActions( &contextMenu );
contextMenu.addSeparator();
float limit = 60000.0f / ( Engine::getSong()->getTempo() *
model()->scale() );
QMenu * syncMenu = contextMenu.addMenu( m_tempoSyncIcon,
m_tempoSyncDescription );
if( limit / 8.0f <= model()->maxValue() )
{
connect( syncMenu, SIGNAL(triggered(QAction*)),
model(), SLOT(setTempoSync(QAction*)));
syncMenu->addAction( embed::getIconPixmap( "note_none" ),
tr( "No Sync" ) )->setData( (int) TempoSyncKnobModel::SyncMode::None );
if( limit / 0.125f <= model()->maxValue() )
{
syncMenu->addAction( embed::getIconPixmap( "note_double_whole" ),
tr( "Eight beats" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::DoubleWholeNote );
}
if( limit / 0.25f <= model()->maxValue() )
{
syncMenu->addAction( embed::getIconPixmap( "note_whole" ),
tr( "Whole note" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::WholeNote );
}
if( limit / 0.5f <= model()->maxValue() )
{
syncMenu->addAction( embed::getIconPixmap( "note_half" ),
tr( "Half note" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::HalfNote );
}
if( limit <= model()->maxValue() )
{
syncMenu->addAction( embed::getIconPixmap( "note_quarter" ),
tr( "Quarter note" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::QuarterNote );
}
if( limit / 2.0f <= model()->maxValue() )
{
syncMenu->addAction( embed::getIconPixmap( "note_eighth" ),
tr( "8th note" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::EighthNote );
}
if( limit / 4.0f <= model()->maxValue() )
{
syncMenu->addAction( embed::getIconPixmap( "note_sixteenth" ),
tr( "16th note" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::SixteenthNote );
}
syncMenu->addAction( embed::getIconPixmap( "note_thirtysecond" ),
tr( "32nd note" ) )->setData(
(int) TempoSyncKnobModel::SyncMode::ThirtysecondNote );
syncMenu->addAction( embed::getIconPixmap( "dont_know" ),
tr( "Custom..." ),
this, SLOT(showCustom())
)->setData(
(int) TempoSyncKnobModel::SyncMode::Custom );
contextMenu.addSeparator();
}
contextMenu.exec( QCursor::pos() );
delete syncMenu;
}
void TempoSyncBarModelEditor::updateDescAndIcon()
{
if( model()->syncMode() != TempoSyncKnobModel::SyncMode::None )
{
switch( model()->syncMode() )
{
case TempoSyncKnobModel::SyncMode::Custom:
m_tempoSyncDescription = tr( "Custom " ) +
"(" +
QString::number( model()->getCustomMeterModel().numeratorModel().value() ) +
"/" +
QString::number( model()->getCustomMeterModel().denominatorModel().value() ) +
")";
break;
case TempoSyncKnobModel::SyncMode::DoubleWholeNote:
m_tempoSyncDescription = tr(
"Synced to Eight Beats" );
break;
case TempoSyncKnobModel::SyncMode::WholeNote:
m_tempoSyncDescription = tr(
"Synced to Whole Note" );
break;
case TempoSyncKnobModel::SyncMode::HalfNote:
m_tempoSyncDescription = tr(
"Synced to Half Note" );
break;
case TempoSyncKnobModel::SyncMode::QuarterNote:
m_tempoSyncDescription = tr(
"Synced to Quarter Note" );
break;
case TempoSyncKnobModel::SyncMode::EighthNote:
m_tempoSyncDescription = tr(
"Synced to 8th Note" );
break;
case TempoSyncKnobModel::SyncMode::SixteenthNote:
m_tempoSyncDescription = tr(
"Synced to 16th Note" );
break;
case TempoSyncKnobModel::SyncMode::ThirtysecondNote:
m_tempoSyncDescription = tr(
"Synced to 32nd Note" );
break;
default: ;
}
}
else
{
m_tempoSyncDescription = tr( "Tempo Sync" );
}
if( m_custom != nullptr &&
model()->syncMode() != TempoSyncKnobModel::SyncMode::Custom )
{
m_custom->parentWidget()->hide();
}
switch( model()->syncMode() )
{
case TempoSyncKnobModel::SyncMode::None:
m_tempoSyncIcon = embed::getIconPixmap( "tempo_sync" );
break;
case TempoSyncKnobModel::SyncMode::Custom:
m_tempoSyncIcon = embed::getIconPixmap( "dont_know" );
break;
case TempoSyncKnobModel::SyncMode::DoubleWholeNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_double_whole" );
break;
case TempoSyncKnobModel::SyncMode::WholeNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_whole" );
break;
case TempoSyncKnobModel::SyncMode::HalfNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_half" );
break;
case TempoSyncKnobModel::SyncMode::QuarterNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_quarter" );
break;
case TempoSyncKnobModel::SyncMode::EighthNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_eighth" );
break;
case TempoSyncKnobModel::SyncMode::SixteenthNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_sixteenth" );
break;
case TempoSyncKnobModel::SyncMode::ThirtysecondNote:
m_tempoSyncIcon = embed::getIconPixmap( "note_thirtysecond" );
break;
default:
qWarning( "TempoSyncKnob::calculateTempoSyncTime:"
"invalid TempoSyncMode" );
break;
}
emit syncDescriptionChanged( m_tempoSyncDescription );
emit syncIconChanged();
}
const QString & TempoSyncBarModelEditor::syncDescription()
{
return m_tempoSyncDescription;
}
void TempoSyncBarModelEditor::setSyncDescription( const QString & _new_description )
{
m_tempoSyncDescription = _new_description;
emit syncDescriptionChanged( _new_description );
}
const QPixmap & TempoSyncBarModelEditor::syncIcon()
{
return m_tempoSyncIcon;
}
void TempoSyncBarModelEditor::setSyncIcon( const QPixmap & _new_icon )
{
m_tempoSyncIcon = _new_icon;
emit syncIconChanged();
}
void TempoSyncBarModelEditor::showCustom()
{
if( m_custom == nullptr )
{
m_custom = new MeterDialog( getGUI()->mainWindow()->workspace() );
QMdiSubWindow * subWindow = getGUI()->mainWindow()->addWindowedWidget( m_custom );
Qt::WindowFlags flags = subWindow->windowFlags();
flags &= ~Qt::WindowMaximizeButtonHint;
subWindow->setWindowFlags( flags );
subWindow->setFixedSize( subWindow->size() );
m_custom->setWindowTitle( "Meter" );
m_custom->setModel( &model()->getCustomMeterModel() );
}
m_custom->parentWidget()->show();
model()->setTempoSync( TempoSyncKnobModel::SyncMode::Custom );
}
} // namespace lmms::gui