Initial global groove quantization feature

Add initial support for "Groove quantizing".  This is a squash of all work by @teknopaul, @tresf, @Sawuare to make it mergabe against master.
This commit is contained in:
Tres Finocchiaro
2018-03-09 11:47:18 -05:00
committed by GitHub
parent d2c370a953
commit 957ec6b611
36 changed files with 2212 additions and 31 deletions

View File

@@ -0,0 +1,62 @@
/*
* AutomatableControlButton.h - A button with a model that accepts
* values from 0 - 127 for MIDI
*
* Copyright (c) 2004-2014 teknopaul <teknopaul/at/users.sourceforge.net>
*
* This file is part of LMMS - http://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 AUTOMATABLE_CONTROL_BUTTON_H
#define AUTOMATABLE_CONTROL_BUTTON_H
#include <QPushButton>
#include "AutomatableModelView.h"
class automatableButtonGroup;
class EXPORT AutomatableControlButton : public QPushButton, public FloatModelView
{
Q_OBJECT
public:
AutomatableControlButton( QWidget * _parent, const QString & _name = QString::null );
virtual ~AutomatableControlButton();
virtual void modelChanged();
public slots:
virtual void update();
protected:
virtual void contextMenuEvent( QContextMenuEvent * _me );
virtual void mousePressEvent( QMouseEvent * _me );
virtual void mouseReleaseEvent( QMouseEvent * _me );
private:
} ;
#endif

71
include/Groove.h Normal file
View File

@@ -0,0 +1,71 @@
/*
* Groove.h - classes for addinng swing/funk/groove/slide (you can't name it but you can feel it)
* to midi which is not precise enough at 192 ticks per tact to make your arse move.
*
* In it simplest terms a groove is a subtle delay on some notes in a pattern.
*
* Copyright (c) 2005-2008 teknopaul <teknopaul/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* 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 GROOVE_H
#define GROOVE_H
#include <QWidget>
#include "lmms_basics.h"
#include "MidiTime.h"
#include "Note.h"
#include "Pattern.h"
#include "SerializingObject.h"
class Groove : public SerializingObject
{
public:
Groove();
/*
* Groove should return true if the note should be played in the curr_time tick,
* at the start of the tick or any time before the next tick.
*
* cur_start - the tick about to be played
* n - the note to be played, or not played
* p - the pattern to which the note belongs
*
* default implementation (no groove) would be return n.pos() == cur_start ? 0 : -1;
*
* returns 0 to play now on the tick, -1 to not play at all and the new offset
* that the note should be shifted if it is to be played later in this tick.
*/
virtual int isInTick( MidiTime * _cur_start, fpp_t _frames, f_cnt_t _offset,
Note * _n, Pattern * _p );
virtual void saveSettings( QDomDocument & _doc, QDomElement & _element );
virtual void loadSettings( const QDomElement & _this );
virtual QWidget * instantiateView( QWidget * _parent );
virtual QString nodeName() const
{
return "none";
}
};
#endif // GROOVE_H

View File

@@ -0,0 +1,74 @@
#ifndef GROOVEEXPERIMENTS_H
#define GROOVEEXPERIMENTS_H
#include <QObject>
#include "Groove.h"
#include "Knob.h"
#include "lmms_basics.h"
#include "MidiTime.h"
#include "Note.h"
#include "Pattern.h"
/**
* A groove thats new
*/
class GrooveExperiments : public QObject, public Groove
{
Q_OBJECT
public:
GrooveExperiments(QObject *parent=0 );
virtual ~GrooveExperiments();
void init();
int amount();
int isInTick(MidiTime * _cur_start, const fpp_t _frames, const f_cnt_t _offset, Note * _n, Pattern * _p );
void loadSettings( const QDomElement & _this );
void saveSettings( QDomDocument & _doc, QDomElement & _element );
inline virtual QString nodeName() const
{
return "experiment";
}
QWidget * instantiateView( QWidget * _parent );
signals:
void shiftAmountChanged(int _newAmount);
public slots:
// valid values are from 0 - 127
void setAmount(int _amount);
void update();
private:
int m_frames_per_tick;
int m_shiftAmount;
float m_shiftFactor;// = (m_shiftAmount / 127.0)
} ;
class GrooveExperimentsView : public QWidget
{
Q_OBJECT
public:
GrooveExperimentsView(GrooveExperiments * _m_ge, QWidget * parent=0 );
~GrooveExperimentsView();
public slots:
void modelChanged();
void valueChanged(float);
private:
GrooveExperiments * m_ge;
FloatModel * m_nobModel;
Knob * m_nob;
} ;
#endif // GROOVEEXPERIMENTS_H

41
include/GrooveFactory.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* Groove.h - classes for addinng swing/funk/groove/slide (you can't name it but you can feel it)
* to midi which is not precise enough at 192 ticks per tact to make your arse move.
*
* In it simplest terms a groove is a subtle delay on some notes in a pattern.
*
* Copyright (c) 2005-2008 teknopaul <teknopaul/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* 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 GROOVEFACTORY_H
#define GROOVEFACTORY_H
#include "Groove.h"
class GrooveFactory
{
public:
static Groove * create(QString type);
private:
GrooveFactory();
};
#endif // GROOVEFACTORY_H

37
include/GrooveView.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef GROOVEVIEW_H
#define GROOVEVIEW_H
#include <QWidget>
#include <QCloseEvent>
#include <QComboBox>
#include <QVBoxLayout>
#include "Groove.h"
#include "SerializingObject.h"
class GrooveView : public QWidget
{
Q_OBJECT
public:
GrooveView();
virtual ~GrooveView();
void clear();
signals:
public slots:
void update();
void grooveChanged(int index);
private:
void setView(Groove * groove);
QComboBox * m_dropDown;
QVBoxLayout * m_layout;
};
#endif // GROOVEVIEW_H

View File

@@ -35,6 +35,8 @@ class AutomationEditorWindow;
class BBEditor;
class ControllerRackView;
class FxMixerView;
class GrooveView;
class StudioControllerView;
class MainWindow;
class PianoRollWindow;
class ProjectNotes;
@@ -51,6 +53,8 @@ public:
MainWindow* mainWindow() { return m_mainWindow; }
FxMixerView* fxMixerView() { return m_fxMixerView; }
GrooveView* grooveView() { return m_grooveView; }
StudioControllerView* studioControllerView() { return m_studioControllerView; }
SongEditorWindow* songEditor() { return m_songEditor; }
BBEditor* getBBEditor() { return m_bbEditor; }
PianoRollWindow* pianoRoll() { return m_pianoRoll; }
@@ -69,6 +73,8 @@ private:
MainWindow* m_mainWindow;
FxMixerView* m_fxMixerView;
GrooveView* m_grooveView;
StudioControllerView* m_studioControllerView;
SongEditorWindow* m_songEditor;
AutomationEditorWindow* m_automationEditor;
BBEditor* m_bbEditor;

72
include/HalfSwing.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef HALFSWING_H
#define HALFSWING_H
#include <QObject>
#include "Groove.h"
#include "Knob.h"
#include "lmms_basics.h"
#include "MidiTime.h"
#include "Note.h"
#include "Pattern.h"
/**
* A groove thatjust latter half of the HydrogenSwing algo.
*/
class HalfSwing : public QObject, public Groove
{
Q_OBJECT
public:
HalfSwing(QObject *parent=0 );
virtual ~HalfSwing();
void init();
int amount();
int isInTick(MidiTime * _cur_start, const fpp_t _frames, const f_cnt_t _offset, Note * _n, Pattern * _p );
void loadSettings( const QDomElement & _this );
void saveSettings( QDomDocument & _doc, QDomElement & _element );
inline virtual QString nodeName() const
{
return "half";
}
QWidget * instantiateView( QWidget * _parent );
signals:
void swingAmountChanged(int _newAmount);
public slots:
// valid values are from 0 - 127
void setAmount(int _amount);
void update();
private:
int m_frames_per_tick;
int m_swingAmount;
float m_swingFactor;// = (m_swingAmount / 127.0)
} ;
class HalfSwingView : public QWidget
{
Q_OBJECT
public:
HalfSwingView(HalfSwing * _half_swing, QWidget * parent=0 );
~HalfSwingView();
public slots:
void modelChanged();
void valueChanged(float);
private:
HalfSwing * m_half_swing;
FloatModel * m_nobModel;
Knob * m_nob;
} ;
#endif // HALFSWING_H

74
include/HydrogenSwing.h Normal file
View File

@@ -0,0 +1,74 @@
#ifndef HYDROGENSWING_H
#define HYDROGENSWING_H
#include <QObject>
#include "Groove.h"
#include "Knob.h"
#include "lmms_basics.h"
#include "MidiTime.h"
#include "Note.h"
#include "Pattern.h"
/**
* A groove that mimics Hydrogen drum machine's swing feature
*/
class HydrogenSwing : public QObject, public Groove
{
Q_OBJECT
public:
HydrogenSwing(QObject *parent=0 );
virtual ~HydrogenSwing();
void init();
int amount();
int isInTick(MidiTime * _cur_start, const fpp_t _frames, const f_cnt_t _offset, Note * _n, Pattern * _p );
void loadSettings( const QDomElement & _this );
void saveSettings( QDomDocument & _doc, QDomElement & _element );
inline virtual QString nodeName() const
{
return "hydrogen";
}
QWidget * instantiateView( QWidget * _parent );
signals:
void swingAmountChanged(int _newAmount);
public slots:
// valid values are from 0 - 127
void setAmount(int _amount);
void update();
private:
int m_frames_per_tick;
int m_swingAmount;
float m_swingFactor;// = (m_swingAmount / 127.0)
} ;
class HydrogenSwingView : public QWidget
{
Q_OBJECT
public:
HydrogenSwingView(HydrogenSwing * _hy_swing, QWidget * parent=0 );
~HydrogenSwingView();
public slots:
void modelChanged();
void valueChanged(float);
private:
HydrogenSwing * m_hy_swing;
FloatModel * m_nobModel;
Knob * m_nob;
} ;
#endif // HYDROGENSWING_H

View File

@@ -27,6 +27,7 @@
#define INSTRUMENT_TRACK_H
#include "AudioPort.h"
#include "Groove.h"
#include "GroupBox.h"
#include "InstrumentFunctions.h"
#include "InstrumentSoundShaping.h"
@@ -90,6 +91,8 @@ public:
f_cnt_t beatLen( NotePlayHandle * _n ) const;
void disableGroove();
void enableGroove();
// for capturing note-play-events -> need that for arpeggio,
// filter and so on
@@ -153,6 +156,8 @@ public:
return &m_audioPort;
}
Groove * groove();
MidiPort * midiPort()
{
return &m_midiPort;
@@ -256,6 +261,11 @@ private:
FloatModel m_panningModel;
AudioPort m_audioPort;
// Track specific groove or NULL
Groove * m_groove;
Groove * m_noGroove;
bool m_grooveOn; //if true temporarily return nooop Groove for the groove
FloatModel m_pitchModel;
IntModel m_pitchRangeModel;
@@ -271,6 +281,7 @@ private:
Piano m_piano;
friend class InstrumentTrackView;
friend class InstrumentTrackWindow;
friend class NotePlayHandle;

View File

@@ -166,6 +166,8 @@ public slots:
void toggleFxMixerWin();
void togglePianoRollWin();
void toggleControllerRack();
void toggleStudioControllerView();
void toggleGrooveView();
void updatePlayPauseIcons();

View File

@@ -53,6 +53,7 @@ public:
}
virtual void saveSettings( QDomDocument & _doc, QDomElement & _this );
virtual void saveControllerSettings( QDomDocument & _doc, QDomElement & _this );
virtual void loadSettings( const QDomElement & _this );
virtual QString nodeName() const;

43
include/MidiSwing.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef MIDISWING_H
#define MIDISWING_H
#include <QObject>
#include "Groove.h"
#include "lmms_basics.h"
#include "MidiTime.h"
#include "Note.h"
#include "Pattern.h"
/*
* A swing groove that adjusts by whole ticks.
* Someone might like it, also might be able to save the output to a midi file later.
*/
class MidiSwing : public QObject, public Groove
{
Q_OBJECT
public:
MidiSwing(QObject * _parent=0 );
~MidiSwing();
// TODO why declaring this should it not come from super class?
int isInTick(MidiTime * cur_start, const fpp_t _frames, const f_cnt_t _offset, Note * n, Pattern * p );
int isInTick(MidiTime * _cur_start, Note * _n, Pattern * _p );
void loadSettings( const QDomElement & _this );
void saveSettings( QDomDocument & _doc, QDomElement & _element );
inline virtual QString nodeName() const
{
return "midi";
}
QWidget * instantiateView( QWidget * _parent );
signals:
public slots:
};
#endif // MIDISWING_H

View File

@@ -35,11 +35,13 @@
#include "MeterModel.h"
#include "Mixer.h"
#include "VstSyncController.h"
#include "Groove.h"
class AutomationTrack;
class Pattern;
class TimeLineWidget;
class Groove;
const bpm_t MinTempo = 10;
@@ -236,6 +238,11 @@ public:
return m_globalAutomationTrack;
}
Groove * globalGroove()
{
return m_globalGroove;
}
//TODO: Add Q_DECL_OVERRIDE when Qt4 is dropped
AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const;
@@ -246,6 +253,7 @@ public:
bool guiSaveProject();
bool guiSaveProjectAs( const QString & filename );
bool saveProjectFile( const QString & filename );
void setGlobalGroove(Groove * groove);
const QString & projectFileName() const
{
@@ -310,6 +318,7 @@ public slots:
void playPattern( const Pattern * patternToPlay, bool loop = true );
void togglePause();
void stop();
void setPlayPos( tick_t ticks, PlayModes playMode );
void startExport();
void stopExport();
@@ -361,8 +370,6 @@ private:
m_playPos[m_playMode].currentFrame();
}
void setPlayPos( tick_t ticks, PlayModes playMode );
void saveControllerStates( QDomDocument & doc, QDomElement & element );
void restoreControllerStates( const QDomElement & element );
@@ -375,6 +382,7 @@ private:
void setProjectFileName(QString const & projectFileName);
AutomationTrack * m_globalAutomationTrack;
Groove * m_globalGroove;
IntModel m_tempoModel;
MeterModel m_timeSigModel;

View File

@@ -110,7 +110,7 @@ private:
virtual void wheelEvent( QWheelEvent * we );
virtual bool allowRubberband() const;
void scrollToPos( const MidiTime & t );
Song * m_song;
@@ -159,11 +159,18 @@ public:
protected:
virtual void resizeEvent( QResizeEvent * event );
protected slots:
public slots:
void play();
void record();
void recordAccompany();
void stop();
void home();
void next();
void prev();
void end();
protected slots:
void recordAccompany();
void lostFocus();
void adjustUiAfterProjectLoad();

View File

@@ -0,0 +1,53 @@
#ifndef STUDIOCONTROLLERVIEW_H
#define STUDIOCONTROLLERVIEW_H
#include <QWidget>
#include <QCloseEvent>
#include <QComboBox>
#include <QVBoxLayout>
#include "SerializingObject.h"
#include "AutomatableControlButton.h"
class StudioControllerView : public QWidget
{
Q_OBJECT
public:
StudioControllerView();
virtual ~StudioControllerView();
signals:
public slots:
void controllerChanged(int index);
void doHome();
void doPlay();
void doStop();
void doRecord();
void doNext();
void doPrev();
void doScroll();
void saveControllers();
void loadControllers();
private:
float m_scrollLast;
QVBoxLayout * m_layout;
QComboBox * m_dropDown;
QLabel * m_controllerLabel;
QLabel * m_actionsLabel;
AutomatableControlButton * m_homeButton;
AutomatableControlButton * m_playButton;
AutomatableControlButton * m_stopButton;
AutomatableControlButton * m_recordButton;
AutomatableControlButton * m_nextButton;
AutomatableControlButton * m_prevButton;
AutomatableControlButton * m_scrollButton;
AutomatableControlButton * m_saveButton;
};
#endif // STUDIOCONTROLLERVIEW_H

View File

@@ -441,6 +441,9 @@ private slots:
void recordingOn();
void recordingOff();
void clearTrack();
QMenu * grooveMenu();
void enableGroove();
void disableGroove();
private:
static QPixmap * s_grip;