mirror of
https://github.com/LMMS/lmms.git
synced 2025-12-30 10:08:00 -05:00
Show ghost notes or sample track as a visual aid in the Automation Editor. --------- Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
379 lines
9.7 KiB
C++
379 lines
9.7 KiB
C++
/*
|
|
* AutomationEditor.h - declaration of class AutomationEditor which is a window
|
|
* where you can edit dynamic values in an easy way
|
|
*
|
|
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
|
|
*
|
|
* 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_AUTOMATION_EDITOR_H
|
|
#define LMMS_GUI_AUTOMATION_EDITOR_H
|
|
|
|
#include <QPushButton>
|
|
#include <QWidget>
|
|
#include <array>
|
|
|
|
#include "AutomationClip.h"
|
|
#include "ComboBoxModel.h"
|
|
#include "Editor.h"
|
|
#include "JournallingObject.h"
|
|
#include "MidiClip.h"
|
|
#include "SampleClip.h"
|
|
#include "TimePos.h"
|
|
#include "lmms_basics.h"
|
|
|
|
class QPainter;
|
|
class QPixmap;
|
|
class QScrollBar;
|
|
|
|
namespace lmms
|
|
{
|
|
|
|
class NotePlayHandle;
|
|
|
|
namespace gui
|
|
{
|
|
|
|
class Knob;
|
|
class ComboBox;
|
|
class TimeLineWidget;
|
|
|
|
|
|
|
|
class AutomationEditor : public QWidget, public JournallingObject
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(QColor barLineColor MEMBER m_barLineColor)
|
|
Q_PROPERTY(QColor beatLineColor MEMBER m_beatLineColor)
|
|
Q_PROPERTY(QColor lineColor MEMBER m_lineColor)
|
|
Q_PROPERTY(QColor nodeInValueColor MEMBER m_nodeInValueColor)
|
|
Q_PROPERTY(QColor nodeOutValueColor MEMBER m_nodeOutValueColor)
|
|
Q_PROPERTY(QColor nodeTangentLineColor MEMBER m_nodeTangentLineColor)
|
|
Q_PROPERTY(QBrush scaleColor MEMBER m_scaleColor)
|
|
Q_PROPERTY(QBrush graphColor MEMBER m_graphColor)
|
|
Q_PROPERTY(QColor crossColor MEMBER m_crossColor)
|
|
Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade)
|
|
Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor)
|
|
Q_PROPERTY(QColor detuningNoteColor MEMBER m_detuningNoteColor)
|
|
Q_PROPERTY(QColor ghostSampleColor MEMBER m_ghostSampleColor)
|
|
public:
|
|
void setCurrentClip(AutomationClip * new_clip);
|
|
void setGhostMidiClip(MidiClip* newMidiClip);
|
|
void setGhostSample(SampleClip* newSample);
|
|
|
|
inline const AutomationClip * currentClip() const
|
|
{
|
|
return m_clip;
|
|
}
|
|
|
|
inline bool validClip() const
|
|
{
|
|
return m_clip != nullptr;
|
|
}
|
|
|
|
void saveSettings(QDomDocument & doc, QDomElement & parent) override;
|
|
void loadSettings(const QDomElement & parent) override;
|
|
QString nodeName() const override
|
|
{
|
|
return "automationeditor";
|
|
}
|
|
|
|
enum class EditMode
|
|
{
|
|
Draw,
|
|
Erase,
|
|
DrawOutValues,
|
|
EditTangents
|
|
};
|
|
|
|
public slots:
|
|
void update();
|
|
void updateAfterClipChange();
|
|
|
|
|
|
protected:
|
|
using timeMap = AutomationClip::timeMap;
|
|
|
|
void keyPressEvent(QKeyEvent * ke) override;
|
|
void leaveEvent(QEvent * e) override;
|
|
void mousePressEvent(QMouseEvent * mouseEvent) override;
|
|
void mouseDoubleClickEvent(QMouseEvent * mouseEvent) override;
|
|
void mouseReleaseEvent(QMouseEvent * mouseEvent) override;
|
|
void mouseMoveEvent(QMouseEvent * mouseEvent) override;
|
|
void paintEvent(QPaintEvent * pe) override;
|
|
void resizeEvent(QResizeEvent * re) override;
|
|
void wheelEvent(QWheelEvent * we) override;
|
|
|
|
float getLevel( int y );
|
|
int xCoordOfTick( int tick );
|
|
float yCoordOfLevel( float level );
|
|
inline void drawLevelTick(QPainter & p, int tick, float value);
|
|
|
|
timeMap::iterator getNodeAt(int x, int y, bool outValue = false, int r = 5);
|
|
/**
|
|
* @brief Given a mouse X coordinate, returns a timeMap::iterator that points to
|
|
* the closest node.
|
|
* @param Int X coordinate
|
|
* @return timeMap::iterator with the closest node or timeMap.end() if there are no nodes.
|
|
*/
|
|
timeMap::iterator getClosestNode(int x);
|
|
|
|
void drawLine( int x0, float y0, int x1, float y1 );
|
|
bool fineTuneValue(timeMap::iterator node, bool editingOutValue);
|
|
|
|
protected slots:
|
|
void play();
|
|
void stop();
|
|
|
|
void horScrolled( int new_pos );
|
|
void verScrolled( int new_pos );
|
|
|
|
void setEditMode(AutomationEditor::EditMode mode);
|
|
void setEditMode(int mode);
|
|
|
|
void setProgressionType(AutomationClip::ProgressionType type);
|
|
/**
|
|
* @brief This method handles the AutomationEditorWindow event of changing
|
|
* progression types. After that, it calls updateEditTanButton so the edit
|
|
* tangents button is updated accordingly
|
|
* @param Int New progression type
|
|
*/
|
|
void setProgressionType(int type);
|
|
void setTension();
|
|
|
|
void updatePosition( const lmms::TimePos & t );
|
|
|
|
void zoomingXChanged();
|
|
void zoomingYChanged();
|
|
|
|
/// Updates the clip's quantization using the current user selected value.
|
|
void setQuantization();
|
|
|
|
void resetGhostNotes()
|
|
{
|
|
m_ghostNotes = nullptr;
|
|
m_ghostSample = nullptr;
|
|
update();
|
|
}
|
|
|
|
private:
|
|
|
|
enum class Action
|
|
{
|
|
None,
|
|
MoveValue,
|
|
EraseValues,
|
|
MoveOutValue,
|
|
ResetOutValues,
|
|
DrawLine,
|
|
MoveTangent,
|
|
ResetTangents
|
|
} ;
|
|
|
|
// some constants...
|
|
static const int SCROLLBAR_SIZE = 12;
|
|
static const int TOP_MARGIN = 16;
|
|
|
|
static const int DEFAULT_Y_DELTA = 6;
|
|
static const int DEFAULT_STEPS_PER_BAR = 16;
|
|
static const int DEFAULT_PPB = 12 * DEFAULT_STEPS_PER_BAR;
|
|
|
|
static const int VALUES_WIDTH = 64;
|
|
|
|
static const int NOTE_HEIGHT = 10; // height of individual notes
|
|
static const int NOTE_MARGIN = 40; // total border margin for notes
|
|
static const int MIN_NOTE_RANGE = 20; // min number of keys for fixed size
|
|
static const int SAMPLE_MARGIN = 40;
|
|
static constexpr int MAX_SAMPLE_HEIGHT = 400; // constexpr for use in min
|
|
|
|
AutomationEditor();
|
|
AutomationEditor( const AutomationEditor & );
|
|
~AutomationEditor() override;
|
|
|
|
QPixmap m_toolDraw = embed::getIconPixmap("edit_draw");
|
|
QPixmap m_toolErase = embed::getIconPixmap("edit_erase");
|
|
QPixmap m_toolDrawOut = embed::getIconPixmap("edit_draw_outvalue");
|
|
QPixmap m_toolEditTangents = embed::getIconPixmap("edit_tangent");
|
|
QPixmap m_toolMove = embed::getIconPixmap("edit_move");
|
|
QPixmap m_toolYFlip = embed::getIconPixmap("flip_y");
|
|
QPixmap m_toolXFlip = embed::getIconPixmap("flip_x");
|
|
|
|
ComboBoxModel m_zoomingXModel;
|
|
ComboBoxModel m_zoomingYModel;
|
|
ComboBoxModel m_quantizeModel;
|
|
|
|
static const std::array<float, 7> m_zoomXLevels;
|
|
|
|
FloatModel * m_tensionModel;
|
|
|
|
AutomationClip * m_clip;
|
|
float m_minLevel;
|
|
float m_maxLevel;
|
|
float m_step;
|
|
float m_scrollLevel;
|
|
float m_bottomLevel;
|
|
float m_topLevel;
|
|
|
|
MidiClip* m_ghostNotes = nullptr;
|
|
QPointer<SampleClip> m_ghostSample = nullptr; // QPointer to set to nullptr on deletion
|
|
bool m_renderSample = false;
|
|
|
|
void centerTopBottomScroll();
|
|
void updateTopBottomLevels();
|
|
|
|
QScrollBar * m_leftRightScroll;
|
|
QScrollBar * m_topBottomScroll;
|
|
|
|
TimePos m_currentPosition;
|
|
|
|
Action m_action;
|
|
|
|
int m_moveXOffset;
|
|
|
|
float m_drawLastLevel;
|
|
tick_t m_drawLastTick;
|
|
|
|
int m_ppb;
|
|
int m_y_delta;
|
|
bool m_y_auto;
|
|
|
|
// Time position (key) of automation node whose outValue is being dragged
|
|
int m_draggedOutValueKey;
|
|
|
|
// The tick from the node whose tangent is being dragged
|
|
int m_draggedTangentTick;
|
|
// Whether the tangent being dragged is the InTangent or OutTangent
|
|
bool m_draggedOutTangent;
|
|
|
|
EditMode m_editMode;
|
|
|
|
bool m_mouseDownLeft;
|
|
bool m_mouseDownRight; //true if right click is being held down
|
|
|
|
TimeLineWidget * m_timeLine;
|
|
bool m_scrollBack;
|
|
|
|
void drawCross(QPainter & p );
|
|
void drawAutomationPoint( QPainter & p, timeMap::iterator it );
|
|
void drawAutomationTangents(QPainter& p, timeMap::iterator it);
|
|
bool inPatternEditor();
|
|
|
|
QColor m_barLineColor;
|
|
QColor m_beatLineColor;
|
|
QColor m_lineColor;
|
|
QBrush m_graphColor;
|
|
QColor m_nodeInValueColor;
|
|
QColor m_nodeOutValueColor;
|
|
QColor m_nodeTangentLineColor;
|
|
QBrush m_scaleColor;
|
|
QColor m_crossColor;
|
|
QColor m_backgroundShade;
|
|
QColor m_ghostNoteColor;
|
|
QColor m_detuningNoteColor;
|
|
QColor m_ghostSampleColor;
|
|
|
|
friend class AutomationEditorWindow;
|
|
|
|
|
|
signals:
|
|
void currentClipChanged();
|
|
void positionChanged( const lmms::TimePos & );
|
|
} ;
|
|
|
|
|
|
|
|
|
|
class AutomationEditorWindow : public Editor
|
|
{
|
|
Q_OBJECT
|
|
|
|
static const int INITIAL_WIDTH = 860;
|
|
static const int INITIAL_HEIGHT = 480;
|
|
public:
|
|
AutomationEditorWindow();
|
|
~AutomationEditorWindow() override = default;
|
|
|
|
void setCurrentClip(AutomationClip* clip);
|
|
void setGhostMidiClip(MidiClip* clip) { m_editor->setGhostMidiClip(clip); };
|
|
void setGhostSample(SampleClip* newSample) { m_editor->setGhostSample(newSample); };
|
|
|
|
const AutomationClip* currentClip();
|
|
|
|
void dropEvent( QDropEvent * _de ) override;
|
|
void dragEnterEvent( QDragEnterEvent * _dee ) override;
|
|
|
|
void open(AutomationClip* clip);
|
|
|
|
AutomationEditor* m_editor;
|
|
|
|
QSize sizeHint() const override;
|
|
|
|
public slots:
|
|
void clearCurrentClip();
|
|
|
|
signals:
|
|
void currentClipChanged();
|
|
|
|
protected:
|
|
void focusInEvent(QFocusEvent * event) override;
|
|
|
|
protected slots:
|
|
void play() override;
|
|
void stop() override;
|
|
|
|
private slots:
|
|
void updateWindowTitle();
|
|
void setProgressionType(int progType);
|
|
/**
|
|
* @brief The Edit Tangent edit mode should only be available for
|
|
* Cubic Hermite progressions, so this method is responsable for disabling it
|
|
* for other edit modes and reenabling it when it changes back to the Edit Tangent
|
|
* mode.
|
|
*/
|
|
void updateEditTanButton();
|
|
|
|
private:
|
|
QAction* m_drawAction;
|
|
QAction* m_eraseAction;
|
|
QAction* m_drawOutAction;
|
|
QAction* m_editTanAction;
|
|
|
|
QAction* m_discreteAction;
|
|
QAction* m_linearAction;
|
|
QAction* m_cubicHermiteAction;
|
|
|
|
QAction* m_flipYAction;
|
|
QAction* m_flipXAction;
|
|
|
|
Knob * m_tensionKnob;
|
|
|
|
ComboBox * m_zoomingXComboBox;
|
|
ComboBox * m_zoomingYComboBox;
|
|
ComboBox * m_quantizeComboBox;
|
|
|
|
QPushButton* m_resetGhostNotes;
|
|
};
|
|
|
|
} // namespace gui
|
|
|
|
} // namespace lmms
|
|
|
|
#endif // LMMS_GUI_AUTOMATION_EDITOR_H
|