Compare commits
26 Commits
fuckass-bu
...
groove
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
013aef5513 | ||
|
|
cbcde94d61 | ||
|
|
dfc7eabcb9 | ||
|
|
9b3fe5e2ad | ||
|
|
94221651d4 | ||
|
|
193c30f888 | ||
|
|
1dbf6aabf7 | ||
|
|
fe9c8b6bf6 | ||
|
|
b2057aee0f | ||
|
|
170c8b9a34 | ||
|
|
14f894c3ee | ||
|
|
6122d0f3f2 | ||
|
|
df329e6f5e | ||
|
|
1b9f28254a | ||
|
|
7117d90c00 | ||
|
|
3760e4642f | ||
|
|
9e23b2a03b | ||
|
|
8fa33a8970 | ||
|
|
81e0fe23b0 | ||
|
|
217c31af8c | ||
|
|
ad9ea6f362 | ||
|
|
d6529ae1e8 | ||
|
|
46a2f78733 | ||
|
|
3866cef8b3 | ||
|
|
46fa1803d3 | ||
|
|
957ec6b611 |
BIN
data/themes/default/groove.png
Normal file
BIN
data/themes/default/groove.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 231 B |
88
include/Groove.h
Normal file
88
include/Groove.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 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 GROOVE_H
|
||||
#define GROOVE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
#include "SerializingObject.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class Groove : public QObject, public SerializingObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Groove(QObject* parent = nullptr);
|
||||
|
||||
/*
|
||||
* 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(TimePos* curStart, fpp_t frames, f_cnt_t offset,
|
||||
Note* n, MidiClip* c);
|
||||
int amount() const { return m_amount; }
|
||||
|
||||
virtual void saveSettings(QDomDocument & doc, QDomElement & element);
|
||||
virtual void loadSettings(const QDomElement & element);
|
||||
|
||||
virtual QWidget* instantiateView(QWidget* parent);
|
||||
|
||||
QString nodeName() const override
|
||||
{
|
||||
return "none";
|
||||
}
|
||||
|
||||
signals:
|
||||
void amountChanged(int newAmount);
|
||||
|
||||
public slots:
|
||||
void setAmount(int amount);
|
||||
|
||||
protected:
|
||||
int m_amount;
|
||||
float m_swingFactor; // = (m_amount / 127)
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // GROOVE_H
|
||||
93
include/GrooveExperiments.h
Normal file
93
include/GrooveExperiments.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* GrooveExperiments.h - A groove that's new
|
||||
*
|
||||
* Copyright (c) 2005-2008 teknopaul <teknopaul/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 GROOVEEXPERIMENTS_H
|
||||
#define GROOVEEXPERIMENTS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "AutomatableSlider.h"
|
||||
#include "Groove.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/**
|
||||
* A groove that's new
|
||||
*/
|
||||
class GrooveExperiments : public Groove
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GrooveExperiments(QObject* parent = nullptr);
|
||||
~GrooveExperiments() override;
|
||||
|
||||
void init();
|
||||
|
||||
int isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset, Note* n, MidiClip* c);
|
||||
|
||||
inline virtual QString nodeName() const
|
||||
{
|
||||
return "experiment";
|
||||
}
|
||||
|
||||
QWidget* instantiateView(QWidget* parent);
|
||||
|
||||
public slots:
|
||||
// valid values are from 0 - 127
|
||||
void update();
|
||||
|
||||
private:
|
||||
int m_framesPerTick;
|
||||
};
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class GrooveExperimentsView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GrooveExperimentsView(GrooveExperiments* groove, QWidget* parent = nullptr);
|
||||
~GrooveExperimentsView() override;
|
||||
|
||||
public slots:
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
GrooveExperiments* m_groove;
|
||||
IntModel* m_sliderModel;
|
||||
AutomatableSlider* m_slider;
|
||||
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // GROOVEEXPERIMENTS_H
|
||||
43
include/GrooveFactory.h
Normal file
43
include/GrooveFactory.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* GrooveFactory.h - a factory class for grooves
|
||||
*
|
||||
* Copyright (c) 2005-2008 teknopaul <teknopaul/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 GROOVEFACTORY_H
|
||||
#define GROOVEFACTORY_H
|
||||
|
||||
#include "Groove.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class GrooveFactory
|
||||
{
|
||||
public:
|
||||
static Groove* create(const QString& grooveType);
|
||||
|
||||
private:
|
||||
GrooveFactory();
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // GROOVEFACTORY_H
|
||||
61
include/GrooveView.h
Normal file
61
include/GrooveView.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* GrooveView.h - a view class for grooves
|
||||
*
|
||||
* Copyright (c) 2005-2008 teknopaul <teknopaul/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 GROOVEVIEW_H
|
||||
#define GROOVEVIEW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QComboBox>
|
||||
|
||||
#include "Groove.h"
|
||||
#include "SerializingObject.h"
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
class GrooveView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GrooveView(QWidget* parent);
|
||||
~GrooveView() override;
|
||||
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void update();
|
||||
void grooveChanged();
|
||||
|
||||
private:
|
||||
void setView(Groove* groove);
|
||||
|
||||
QVBoxLayout* m_layout;
|
||||
QComboBox* m_comboBox;
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // GROOVEVIEW_H
|
||||
@@ -37,6 +37,7 @@ namespace lmms::gui
|
||||
|
||||
class AutomationEditorWindow;
|
||||
class ControllerRackView;
|
||||
class GrooveView;
|
||||
class MixerView;
|
||||
class MainWindow;
|
||||
class MicrotunerConfig;
|
||||
@@ -58,6 +59,7 @@ public:
|
||||
#endif
|
||||
|
||||
MainWindow* mainWindow() { return m_mainWindow; }
|
||||
GrooveView* grooveView() { return m_grooveView; }
|
||||
MixerView* mixerView() { return m_mixerView; }
|
||||
SongEditorWindow* songEditor() { return m_songEditor; }
|
||||
PatternEditorWindow* patternEditor() { return m_patternEditor; }
|
||||
@@ -77,6 +79,7 @@ private:
|
||||
static GuiApplication* s_instance;
|
||||
|
||||
MainWindow* m_mainWindow;
|
||||
GrooveView* m_grooveView;
|
||||
MixerView* m_mixerView;
|
||||
SongEditorWindow* m_songEditor;
|
||||
AutomationEditorWindow* m_automationEditor;
|
||||
|
||||
92
include/HalfSwing.h
Normal file
92
include/HalfSwing.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* HalfSwing.h - A groove that uses just the latter half of the Hydrogen Swing algorithm
|
||||
*
|
||||
* Copyright (c) 2005-2008 teknopaul <teknopaul/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 HALFSWING_H
|
||||
#define HALFSWING_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "AutomatableSlider.h"
|
||||
#include "Groove.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/**
|
||||
* A groove that uses just the latter half of the Hydrogen Swing algorithm
|
||||
*/
|
||||
class HalfSwing : public Groove
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HalfSwing(QObject* parent = nullptr);
|
||||
~HalfSwing() override;
|
||||
|
||||
void init();
|
||||
|
||||
int isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset, Note* n, MidiClip* c);
|
||||
|
||||
QString nodeName() const override
|
||||
{
|
||||
return "half";
|
||||
}
|
||||
|
||||
QWidget* instantiateView(QWidget* parent);
|
||||
|
||||
public slots:
|
||||
// valid values are from 0 - 127
|
||||
void update();
|
||||
|
||||
private:
|
||||
int m_framesPerTick;
|
||||
} ;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class HalfSwingView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HalfSwingView(HalfSwing* halfSwing, QWidget* parent = nullptr);
|
||||
~HalfSwingView() override;
|
||||
|
||||
public slots:
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
HalfSwing* m_swing;
|
||||
IntModel* m_sliderModel;
|
||||
AutomatableSlider* m_slider;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // HALFSWING_H
|
||||
95
include/HydrogenSwing.h
Normal file
95
include/HydrogenSwing.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* HydrogenSwing.h - A groove that mimics Hydrogen drum machine's swing feature
|
||||
*
|
||||
* Copyright (c) 2005-2008 teknopaul <teknopaul/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 HYDROGENSWING_H
|
||||
#define HYDROGENSWING_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "AutomatableSlider.h"
|
||||
#include "Groove.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/**
|
||||
* A groove that mimics Hydrogen drum machine's swing feature
|
||||
*/
|
||||
class HydrogenSwing : public Groove
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HydrogenSwing(QObject *parent = nullptr);
|
||||
~HydrogenSwing() override;
|
||||
|
||||
void init();
|
||||
|
||||
int isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset, Note* n, MidiClip* c);
|
||||
|
||||
QString nodeName() const override
|
||||
{
|
||||
return "hydrogen";
|
||||
}
|
||||
|
||||
|
||||
|
||||
QWidget* instantiateView(QWidget* parent);
|
||||
|
||||
public slots:
|
||||
// valid values are from 0 - 127
|
||||
void update();
|
||||
|
||||
private:
|
||||
int m_framesPerTick;
|
||||
} ;
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class HydrogenSwingView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HydrogenSwingView(HydrogenSwing* swing, QWidget* parent = NULL);
|
||||
~HydrogenSwingView();
|
||||
|
||||
public slots:
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
HydrogenSwing* m_swing;
|
||||
IntModel* m_sliderModel;
|
||||
AutomatableSlider* m_slider;
|
||||
|
||||
} ;
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // HYDROGENSWING_H
|
||||
@@ -69,8 +69,7 @@ private:
|
||||
QToolButton * m_wpBtn;
|
||||
|
||||
LcdSpinBox* m_baseVelocitySpinBox;
|
||||
|
||||
} ;
|
||||
};
|
||||
|
||||
|
||||
} // namespace gui
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define LMMS_INSTRUMENT_TRACK_H
|
||||
|
||||
#include "AudioPort.h"
|
||||
#include "Groove.h"
|
||||
#include "InstrumentFunctions.h"
|
||||
#include "InstrumentSoundShaping.h"
|
||||
#include "Microtuner.h"
|
||||
@@ -83,6 +84,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
|
||||
@@ -148,6 +151,8 @@ public:
|
||||
return &m_audioPort;
|
||||
}
|
||||
|
||||
Groove * groove();
|
||||
|
||||
MidiPort * midiPort()
|
||||
{
|
||||
return &m_midiPort;
|
||||
@@ -294,10 +299,16 @@ private:
|
||||
|
||||
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;
|
||||
IntModel m_mixerChannelModel;
|
||||
BoolModel m_useMasterPitchModel;
|
||||
BoolModel m_useGrooveModel;
|
||||
|
||||
Instrument * m_instrument;
|
||||
InstrumentSoundShaping m_soundShaping;
|
||||
@@ -317,7 +328,9 @@ private:
|
||||
friend class gui::InstrumentTuningView;
|
||||
friend class gui::MidiCCRackView;
|
||||
|
||||
} ;
|
||||
private slots:
|
||||
void updateGroove();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
|
||||
LedCheckBox *rangeImportCheckbox() {return m_rangeImportCheckbox;}
|
||||
|
||||
GroupBox *grooveGroupBox() {return m_grooveGroupBox;}
|
||||
|
||||
private:
|
||||
GroupBox *m_pitchGroupBox;
|
||||
GroupBox *m_microtunerGroupBox;
|
||||
@@ -70,6 +72,8 @@ private:
|
||||
ComboBox *m_keymapCombo;
|
||||
|
||||
LedCheckBox *m_rangeImportCheckbox;
|
||||
|
||||
GroupBox * m_grooveGroupBox;
|
||||
};
|
||||
|
||||
|
||||
|
||||
63
include/MidiSwing.h
Normal file
63
include/MidiSwing.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* MidiSwing.h - A swing groove that adjusts by whole ticks
|
||||
*
|
||||
* Copyright (c) 2005-2008 teknopaul <teknopaul/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 MIDISWING_H
|
||||
#define MIDISWING_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Groove.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/*
|
||||
* 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 Groove
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MidiSwing(QObject* parent = nullptr);
|
||||
~MidiSwing() override;
|
||||
|
||||
// TODO why declaring this should it not come from super class?
|
||||
int isInTick(TimePos* cur_start, const fpp_t frames, const f_cnt_t offset, Note* n, MidiClip* c);
|
||||
int isInTick(TimePos* curStart, Note* n, MidiClip* c);
|
||||
|
||||
QString nodeName() const override
|
||||
{
|
||||
return "midi";
|
||||
}
|
||||
|
||||
QWidget* instantiateView(QWidget* parent);
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // MIDISWING_H
|
||||
@@ -38,11 +38,13 @@
|
||||
#include "Timeline.h"
|
||||
#include "TrackContainer.h"
|
||||
#include "VstSyncController.h"
|
||||
#include "Groove.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class AutomationTrack;
|
||||
class Groove;
|
||||
class Keymap;
|
||||
class MidiClip;
|
||||
class Scale;
|
||||
@@ -292,6 +294,11 @@ public:
|
||||
return m_globalAutomationTrack;
|
||||
}
|
||||
|
||||
Groove* globalGroove()
|
||||
{
|
||||
return m_globalGroove;
|
||||
}
|
||||
|
||||
//TODO: Add Q_DECL_OVERRIDE when Qt4 is dropped
|
||||
AutomatedValueMap automatedValuesAt(TimePos time, int clipNum = -1) const override;
|
||||
|
||||
@@ -302,6 +309,7 @@ public:
|
||||
bool guiSaveProject();
|
||||
bool guiSaveProjectAs(const QString & filename);
|
||||
bool saveProjectFile(const QString & filename, bool withResources = false);
|
||||
void setGlobalGroove(Groove* groove);
|
||||
|
||||
const QString & projectFileName() const
|
||||
{
|
||||
@@ -383,6 +391,7 @@ public slots:
|
||||
void playMidiClip( const lmms::MidiClip * midiClipToPlay, bool loop = true );
|
||||
void togglePause();
|
||||
void stop();
|
||||
void setPlayPos( tick_t ticks, PlayMode playMode );
|
||||
|
||||
void startExport();
|
||||
void stopExport();
|
||||
@@ -434,8 +443,6 @@ private:
|
||||
getPlayPos(m_playMode).currentFrame();
|
||||
}
|
||||
|
||||
void setPlayPos( tick_t ticks, PlayMode playMode );
|
||||
|
||||
void saveControllerStates( QDomDocument & doc, QDomElement & element );
|
||||
void restoreControllerStates( const QDomElement & element );
|
||||
|
||||
@@ -454,6 +461,7 @@ private:
|
||||
void setProjectFileName(QString const & projectFileName);
|
||||
|
||||
AutomationTrack * m_globalAutomationTrack;
|
||||
Groove* m_globalGroove;
|
||||
|
||||
IntModel m_tempoModel;
|
||||
MeterModel m_timeSigModel;
|
||||
|
||||
@@ -24,6 +24,11 @@ set(LMMS_SRCS
|
||||
core/EnvelopeAndLfoParameters.cpp
|
||||
core/fft_helpers.cpp
|
||||
core/Mixer.cpp
|
||||
core/Groove.cpp
|
||||
core/GrooveExperiments.cpp
|
||||
core/GrooveFactory.cpp
|
||||
core/HalfSwing.cpp
|
||||
core/HydrogenSwing.cpp
|
||||
core/ImportFilter.cpp
|
||||
core/InlineAutomation.cpp
|
||||
core/Instrument.cpp
|
||||
@@ -40,6 +45,7 @@ set(LMMS_SRCS
|
||||
core/LocklessAllocator.cpp
|
||||
core/MemoryHelper.cpp
|
||||
core/MeterModel.cpp
|
||||
core/MidiSwing.cpp
|
||||
core/MicroTimer.cpp
|
||||
core/Microtuner.cpp
|
||||
core/MixHelpers.cpp
|
||||
|
||||
70
src/core/Groove.cpp
Normal file
70
src/core/Groove.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <QDomElement>
|
||||
#include <QObject>
|
||||
#include <QLabel>
|
||||
|
||||
#include "Groove.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
#include "Song.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
Groove::Groove(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Default groove is no groove. Not even a wiggle.
|
||||
* @return 0 or -1
|
||||
*/
|
||||
int Groove::isInTick(TimePos* curStart, fpp_t frames, f_cnt_t offset,
|
||||
Note* n, MidiClip* c)
|
||||
{
|
||||
return n->pos().getTicks() == curStart->getTicks() ? 0 : -1;
|
||||
}
|
||||
|
||||
void Groove::setAmount(int amount)
|
||||
{
|
||||
if (amount < 0) {
|
||||
amount = 0;
|
||||
}
|
||||
if (amount > 127) {
|
||||
amount = 127;
|
||||
}
|
||||
|
||||
m_amount = amount;
|
||||
m_swingFactor = ((float)m_amount) / 127.0f;
|
||||
emit amountChanged(m_amount);
|
||||
|
||||
}
|
||||
|
||||
void Groove::saveSettings(QDomDocument & doc, QDomElement & element)
|
||||
{
|
||||
Q_UNUSED(doc);
|
||||
element.setAttribute("amount", m_amount);
|
||||
}
|
||||
|
||||
void Groove::loadSettings(const QDomElement & element)
|
||||
{
|
||||
bool ok;
|
||||
int amount = element.attribute("amount").toInt(&ok);
|
||||
if (ok)
|
||||
{
|
||||
setAmount(amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAmount(0);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* Groove::instantiateView(QWidget* parent)
|
||||
{
|
||||
return new QLabel("");
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
167
src/core/GrooveExperiments.cpp
Normal file
167
src/core/GrooveExperiments.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* GrooveExperiments.cpp - Try to find new groove algos that sound interesting
|
||||
*
|
||||
* Copyright (c) 2004-2014 teknopaul <teknopaul/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.
|
||||
*
|
||||
*/
|
||||
#include <QObject>
|
||||
#include <QLabel>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Groove.h"
|
||||
#include "GrooveExperiments.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
#include "Song.h"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
GrooveExperiments::GrooveExperiments(QObject* parent) :
|
||||
Groove(parent)
|
||||
{
|
||||
m_amount = 0;
|
||||
m_swingFactor = 0.0;
|
||||
init();
|
||||
update();
|
||||
}
|
||||
|
||||
GrooveExperiments::~GrooveExperiments()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void GrooveExperiments::init()
|
||||
{
|
||||
|
||||
Song* s = Engine::getSong();
|
||||
connect(s, SIGNAL(projectLoaded()), this, SLOT(update()));
|
||||
connect(s, SIGNAL(lengthChanged(int)), this, SLOT(update()));
|
||||
connect(s, SIGNAL(tempoChanged(lmms::bpm_t)), this, SLOT(update()));
|
||||
connect(s, SIGNAL(timeSignatureChanged(int, int)), this, SLOT(update()));
|
||||
|
||||
}
|
||||
|
||||
void GrooveExperiments::update()
|
||||
{
|
||||
m_framesPerTick = Engine::framesPerTick();
|
||||
}
|
||||
|
||||
int GrooveExperiments::isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset, Note* n, MidiClip* c)
|
||||
{
|
||||
// TODO why is this wrong on boot how do we set it once not every loop
|
||||
if (m_framesPerTick == 0)
|
||||
{
|
||||
m_framesPerTick = Engine::framesPerTick(); // e.g. 500 at 120BPM 4/4
|
||||
}
|
||||
|
||||
// only ever delay notes by 12 ticks, so if the tick is earlier don't play
|
||||
if (n->pos().getTicks() + 12 < curStart->getTicks())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// else work out how much to offset the start point.
|
||||
|
||||
// Where are we in the beat
|
||||
// 48 ticks to the beat, 192 ticks to the bar
|
||||
int posInBeat = n->pos().getTicks() % 48;
|
||||
|
||||
|
||||
int posInEigth = -1;
|
||||
if (posInBeat >= 36 && posInBeat < 48)
|
||||
{
|
||||
// third quarter
|
||||
posInEigth = posInBeat - 36; // 0-11
|
||||
}
|
||||
|
||||
if (posInEigth >= 0)
|
||||
{
|
||||
|
||||
float ticksToShift = ((posInEigth - 12) * -m_swingFactor);
|
||||
|
||||
f_cnt_t framesToShift = (int)(ticksToShift* m_framesPerTick);
|
||||
|
||||
int tickOffset = (int)(framesToShift / m_framesPerTick); // round down
|
||||
|
||||
if (curStart->getTicks() == (n->pos().getTicks() + tickOffset))
|
||||
{
|
||||
// play in this tick
|
||||
|
||||
f_cnt_t newOffset = (framesToShift % m_framesPerTick) + offset;
|
||||
|
||||
return newOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this note does not play in this tick
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// else no groove adjustments
|
||||
return n->pos().getTicks() == curStart->getTicks() ? 0 : -1;
|
||||
}
|
||||
|
||||
QWidget* GrooveExperiments::instantiateView(QWidget* parent)
|
||||
{
|
||||
return new gui::GrooveExperimentsView(this, parent);
|
||||
}
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
// VIEW //
|
||||
|
||||
GrooveExperimentsView::GrooveExperimentsView(GrooveExperiments* groove, QWidget* parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
m_sliderModel = new IntModel(0, 0, 127); // Unused
|
||||
m_sliderModel->setValue(groove->amount());
|
||||
m_slider = new AutomatableSlider(this, tr("Swinginess"));
|
||||
m_slider->setOrientation(Qt::Horizontal);
|
||||
m_slider->setFixedSize(90, 26);
|
||||
m_slider->setPageStep(1);
|
||||
m_slider->setModel(m_sliderModel);
|
||||
|
||||
m_groove = groove;
|
||||
|
||||
connect(m_sliderModel, SIGNAL(dataChanged()), this, SLOT(valueChanged()));
|
||||
}
|
||||
|
||||
GrooveExperimentsView::~GrooveExperimentsView()
|
||||
{
|
||||
delete m_slider;
|
||||
delete m_sliderModel;
|
||||
}
|
||||
|
||||
void GrooveExperimentsView::valueChanged()
|
||||
{
|
||||
m_groove->setAmount(m_sliderModel->value());
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
43
src/core/GrooveFactory.cpp
Normal file
43
src/core/GrooveFactory.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
#include "GrooveFactory.h"
|
||||
|
||||
#include "Groove.h"
|
||||
#include "GrooveExperiments.h"
|
||||
#include "MidiSwing.h"
|
||||
#include "HalfSwing.h"
|
||||
#include "HydrogenSwing.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
GrooveFactory::GrooveFactory()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create grooves classes of the correct type.
|
||||
* grooveType should match the string returned by the grooves nodeName() method
|
||||
*
|
||||
* TODO this is a bit Java-like how does C++ do this kind of thing normally
|
||||
*/
|
||||
Groove* GrooveFactory::create(const QString& grooveType) {
|
||||
if (grooveType.isEmpty() || grooveType == "none") {
|
||||
return new Groove();
|
||||
}
|
||||
if (grooveType == "hydrogen") {
|
||||
return new HydrogenSwing();
|
||||
}
|
||||
if (grooveType == "midi") {
|
||||
return new MidiSwing();
|
||||
}
|
||||
if (grooveType == "half") {
|
||||
return new HalfSwing();
|
||||
}
|
||||
if (grooveType == "experiment") {
|
||||
return new GrooveExperiments();
|
||||
}
|
||||
return new Groove();
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
175
src/core/HalfSwing.cpp
Normal file
175
src/core/HalfSwing.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* HalfSwing.cpp - Swing algo that varies adjustments form 0-127
|
||||
* The algorythm is just the latter half of the HydrogenSwing groove..
|
||||
*
|
||||
* Copyright (c) 2004-2014 teknopaul <teknopaul/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.
|
||||
*
|
||||
*/
|
||||
#include <QObject>
|
||||
#include <QLabel>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Groove.h"
|
||||
#include "HalfSwing.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
#include "Song.h"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
HalfSwing::HalfSwing(QObject* parent) :
|
||||
Groove(parent)
|
||||
{
|
||||
m_amount = 0;
|
||||
m_swingFactor = 0.0;
|
||||
init();
|
||||
update();
|
||||
}
|
||||
|
||||
HalfSwing::~HalfSwing()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void HalfSwing::init()
|
||||
{
|
||||
|
||||
Song* s = Engine::getSong();
|
||||
connect(s, SIGNAL(projectLoaded()), this, SLOT(update()));
|
||||
connect(s, SIGNAL(lengthChanged(int)), this, SLOT(update()));
|
||||
connect(s, SIGNAL(tempoChanged(lmms::bpm_t)), this, SLOT(update()));
|
||||
connect(s, SIGNAL(timeSignatureChanged(int, int)), this, SLOT(update()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void HalfSwing::update()
|
||||
{
|
||||
m_framesPerTick = Engine::framesPerTick();
|
||||
}
|
||||
|
||||
|
||||
int HalfSwing::isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset,
|
||||
Note* n, MidiClip* c)
|
||||
{
|
||||
// TODO why is this wrong on boot how do we set it once not every loop
|
||||
if (m_framesPerTick == 0)
|
||||
{
|
||||
m_framesPerTick = Engine::framesPerTick(); // e.g. 500 at 120BPM 4/4
|
||||
}
|
||||
|
||||
// only ever delay notes by 7 ticks, so if the tick is earlier don't play
|
||||
if (n->pos().getTicks() + 7 < curStart->getTicks())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// else work out how much to offset the start point.
|
||||
|
||||
// Where are we in the beat
|
||||
// 48 ticks to the beat, 192 ticks to the bar
|
||||
int pos_in_beat = n->pos().getTicks() % 48;
|
||||
|
||||
|
||||
// The Half Swing algorithm.
|
||||
// Basically we delay (shift) notes on the the 4th quarter of the beat.
|
||||
|
||||
int posInEigth = -1;
|
||||
if (pos_in_beat >= 36 && pos_in_beat < 42)
|
||||
{
|
||||
// 1st half of third quarter
|
||||
posInEigth = pos_in_beat - 36; // 0-5
|
||||
}
|
||||
|
||||
if (posInEigth >= 0)
|
||||
{
|
||||
|
||||
float ticksToShift = ((posInEigth - 6) * -m_swingFactor);
|
||||
|
||||
f_cnt_t framesToShift = (int)(ticksToShift* m_framesPerTick);
|
||||
|
||||
int tickOffset = (int)(framesToShift / m_framesPerTick); // round down
|
||||
|
||||
if (curStart->getTicks() == (n->pos().getTicks() + tickOffset))
|
||||
{
|
||||
// play in this tick
|
||||
|
||||
f_cnt_t newOffset = (framesToShift % m_framesPerTick) + offset;
|
||||
|
||||
return newOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this note does not play in this tick
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// else no groove adjustments
|
||||
return n->pos().getTicks() == curStart->getTicks() ? 0 : -1;
|
||||
}
|
||||
|
||||
QWidget* HalfSwing::instantiateView(QWidget* parent)
|
||||
{
|
||||
return new gui::HalfSwingView(this, parent);
|
||||
}
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
// VIEW //
|
||||
|
||||
HalfSwingView::HalfSwingView(HalfSwing* halfSwing, QWidget* parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
m_sliderModel = new IntModel(0, 0, 127); // Unused
|
||||
m_sliderModel->setValue(halfSwing->amount());
|
||||
m_slider = new AutomatableSlider(this, tr("Swinginess"));
|
||||
m_slider->setOrientation(Qt::Horizontal);
|
||||
m_slider->setFixedSize(90, 26);
|
||||
m_slider->setPageStep(1);
|
||||
m_slider->setModel(m_sliderModel);
|
||||
|
||||
m_swing = halfSwing;
|
||||
|
||||
connect(m_sliderModel, SIGNAL(dataChanged()), this, SLOT(valueChanged()));
|
||||
}
|
||||
|
||||
HalfSwingView::~HalfSwingView()
|
||||
{
|
||||
delete m_slider;
|
||||
delete m_sliderModel;
|
||||
}
|
||||
|
||||
void HalfSwingView::valueChanged()
|
||||
{
|
||||
m_swing->setAmount(m_sliderModel->value());
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
178
src/core/HydrogenSwing.cpp
Normal file
178
src/core/HydrogenSwing.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* HydrogenSwing.cpp - Swing algo that varies adjustments form 0-127
|
||||
* The algorythm mimics Hydrogen drum machines swing feature.
|
||||
*
|
||||
* Copyright (c) 2004-2014 teknopaul <teknopaul/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.
|
||||
*
|
||||
*/
|
||||
#include <QObject>
|
||||
#include <QLabel>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Groove.h"
|
||||
#include "HydrogenSwing.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "TimePos.h"
|
||||
#include "Note.h"
|
||||
#include "MidiClip.h"
|
||||
#include "Song.h"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
HydrogenSwing::HydrogenSwing(QObject* _parent) :
|
||||
Groove(_parent)
|
||||
{
|
||||
m_amount = 0;
|
||||
m_swingFactor = 0.0;
|
||||
init();
|
||||
update();
|
||||
}
|
||||
|
||||
HydrogenSwing::~HydrogenSwing()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void HydrogenSwing::init()
|
||||
{
|
||||
|
||||
Song* s = Engine::getSong();
|
||||
connect(s, SIGNAL(projectLoaded()), this, SLOT(update()));
|
||||
connect(s, SIGNAL(lengthChanged(int)), this, SLOT(update()));
|
||||
connect(s, SIGNAL(tempoChanged(lmms::bpm_t)), this, SLOT(update()));
|
||||
connect(s, SIGNAL(timeSignatureChanged(int, int)), this, SLOT(update()));
|
||||
|
||||
}
|
||||
|
||||
void HydrogenSwing::update()
|
||||
{
|
||||
m_framesPerTick = Engine::framesPerTick();
|
||||
}
|
||||
|
||||
int HydrogenSwing::isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset,
|
||||
Note* n, MidiClip* c)
|
||||
{
|
||||
// TODO why is this wrong on boot how do we set it once not every loop
|
||||
if (m_framesPerTick == 0)
|
||||
{
|
||||
m_framesPerTick = Engine::framesPerTick(); // e.g. 500 at 120BPM 4/4
|
||||
}
|
||||
|
||||
// only ever delay notes by 7 ticks, so if the tick is earlier don't play
|
||||
if (n->pos().getTicks() + 7 < curStart->getTicks())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// else work out how much to offset the start point.
|
||||
|
||||
// Where are we in the beat
|
||||
// 48 ticks to the beat, 192 ticks to the bar
|
||||
int posInBeat = n->pos().getTicks() % 48;
|
||||
|
||||
|
||||
// The Hydrogen Swing algorithm.
|
||||
// Guessed by turning the knob and watching the possitions change in Audacity.
|
||||
// Basically we delay (shift) notes on the the 2nd and 4th quarter of the beat.
|
||||
|
||||
int posInEigth = -1;
|
||||
if (posInBeat >= 12 && posInBeat < 18)
|
||||
{
|
||||
// 1st half of second quarter
|
||||
posInEigth = posInBeat - 12; // 0-5
|
||||
}
|
||||
else if (posInBeat >= 36 && posInBeat < 42)
|
||||
{
|
||||
// 1st half of third quarter
|
||||
posInEigth = posInBeat - 36; // 0-5
|
||||
}
|
||||
|
||||
if (posInEigth >= 0)
|
||||
{
|
||||
|
||||
float ticksToShift = ((posInEigth - 6) * -m_swingFactor);
|
||||
|
||||
f_cnt_t framesToShift = (int)(ticksToShift* m_framesPerTick);
|
||||
|
||||
int tickOffset = (int)(framesToShift / m_framesPerTick); // round down
|
||||
|
||||
if (curStart->getTicks() == (n->pos().getTicks() + tickOffset))
|
||||
{
|
||||
// play in this tick
|
||||
|
||||
f_cnt_t newOffset = (framesToShift % m_framesPerTick) + offset;
|
||||
|
||||
return newOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this note does not play in this tick
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// else no groove adjustments
|
||||
return n->pos().getTicks() == curStart->getTicks() ? 0 : -1;
|
||||
}
|
||||
|
||||
QWidget* HydrogenSwing::instantiateView(QWidget* parent)
|
||||
{
|
||||
return new gui::HydrogenSwingView(this, parent);
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
// VIEW //
|
||||
|
||||
HydrogenSwingView::HydrogenSwingView(HydrogenSwing* swing, QWidget* parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
m_sliderModel = new IntModel(0, 0, 127); // Unused
|
||||
m_sliderModel->setValue(swing->amount());
|
||||
m_slider = new AutomatableSlider(this, tr("Swinginess"));
|
||||
m_slider->setOrientation(Qt::Horizontal);
|
||||
m_slider->setFixedSize(90, 26);
|
||||
m_slider->setPageStep(1);
|
||||
m_slider->setModel(m_sliderModel);
|
||||
|
||||
m_swing = swing;
|
||||
|
||||
connect(m_sliderModel, SIGNAL(dataChanged()), this, SLOT(valueChanged()));
|
||||
}
|
||||
|
||||
HydrogenSwingView::~HydrogenSwingView()
|
||||
{
|
||||
delete m_slider;
|
||||
delete m_sliderModel;
|
||||
}
|
||||
|
||||
void HydrogenSwingView::valueChanged()
|
||||
{
|
||||
m_swing->setAmount(m_sliderModel->value());
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
96
src/core/MidiSwing.cpp
Normal file
96
src/core/MidiSwing.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* MidiSwing.cpp - Swing algo using fixed integer step adjustments that
|
||||
* could be represented as midi.
|
||||
*
|
||||
* Copyright (c) 2004-2014 teknopaul <teknopaul/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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
#include "MidiSwing.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
MidiSwing::MidiSwing(QObject* parent) :
|
||||
Groove(parent)
|
||||
{
|
||||
}
|
||||
|
||||
MidiSwing::~MidiSwing()
|
||||
{
|
||||
}
|
||||
|
||||
static int applyMidiSwing(int posInEight);
|
||||
|
||||
int MidiSwing::isInTick(TimePos* curStart, const fpp_t frames, const f_cnt_t offset,
|
||||
Note* n, MidiClip* c)
|
||||
{
|
||||
return isInTick(curStart, n, c);
|
||||
}
|
||||
|
||||
int MidiSwing::isInTick(TimePos* curStart, Note* n, MidiClip* c)
|
||||
{
|
||||
|
||||
// Where are we in the beat
|
||||
int posInBeat = n->pos().getTicks() % 48; // assumes 48 ticks per beat, todo verify this
|
||||
|
||||
|
||||
// the Midi Swing algorithm.
|
||||
|
||||
int posInEigth = -1;
|
||||
if (posInBeat >= 12 && posInBeat < 18)
|
||||
{
|
||||
// 1st half of second quarter
|
||||
// add a 0 - 24 tick shift
|
||||
posInEigth = posInBeat - 12; // 0-5
|
||||
}
|
||||
else if (posInBeat >= 36 && posInBeat < 42)
|
||||
{
|
||||
// 1st half of third quarter
|
||||
posInEigth = posInBeat - 36; // 0-5
|
||||
}
|
||||
|
||||
int swingTicks = applyMidiSwing(posInEigth);
|
||||
|
||||
return curStart->getTicks() == swingTicks + n->pos().getTicks() ? 0 : -1;
|
||||
|
||||
}
|
||||
|
||||
QWidget* MidiSwing::instantiateView(QWidget* parent)
|
||||
{
|
||||
return new QLabel("");
|
||||
}
|
||||
|
||||
static int applyMidiSwing(int posInEight)
|
||||
{
|
||||
// TODO case
|
||||
if (posInEight < 0) return 0;
|
||||
if (posInEight == 0) return 3;
|
||||
if (posInEight == 1) return 3;
|
||||
if (posInEight == 2) return 4;
|
||||
if (posInEight == 3) return 4;
|
||||
if (posInEight == 4) return 5;
|
||||
if (posInEight == 5) return 5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "EnvelopeAndLfoParameters.h"
|
||||
#include "Mixer.h"
|
||||
#include "MixerView.h"
|
||||
#include "GrooveFactory.h"
|
||||
#include "GrooveView.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "ExportFilter.h"
|
||||
#include "InstrumentTrack.h"
|
||||
@@ -70,6 +72,7 @@ Song::Song() :
|
||||
m_globalAutomationTrack( dynamic_cast<AutomationTrack *>(
|
||||
Track::create( Track::Type::HiddenAutomation,
|
||||
this ) ) ),
|
||||
m_globalGroove(GrooveFactory::create("none")),
|
||||
m_tempoModel( DefaultTempo, MinTempo, MaxTempo, this, tr( "Tempo" ) ),
|
||||
m_timeSigModel( this ),
|
||||
m_oldTicksPerBar( DefaultTicksPerBar ),
|
||||
@@ -130,6 +133,7 @@ Song::~Song()
|
||||
{
|
||||
m_playing = false;
|
||||
delete m_globalAutomationTrack;
|
||||
delete m_globalGroove;
|
||||
}
|
||||
|
||||
|
||||
@@ -603,8 +607,6 @@ void Song::setPlayPos( tick_t ticks, PlayMode playMode )
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Song::togglePause()
|
||||
{
|
||||
if( m_paused == true )
|
||||
@@ -1088,6 +1090,18 @@ void Song::loadProject( const QString & fileName )
|
||||
}
|
||||
}
|
||||
|
||||
node = dataFile.content().firstChildElement( "groove" );
|
||||
if( !node.isNull() )
|
||||
{
|
||||
QDomElement ge = dataFile.content().firstChildElement( "groove" );
|
||||
m_globalGroove = GrooveFactory::create(ge.attribute("type"));
|
||||
m_globalGroove->restoreState( ge.firstChildElement(ge.attribute("type")) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_globalGroove = GrooveFactory::create("none");
|
||||
}
|
||||
|
||||
node = dataFile.content().firstChild();
|
||||
|
||||
QDomNodeList tclist=dataFile.content().elementsByTagName("trackcontainer");
|
||||
@@ -1227,6 +1241,12 @@ bool Song::saveProjectFile(const QString & filename, bool withResources)
|
||||
saveState( dataFile, dataFile.content() );
|
||||
|
||||
m_globalAutomationTrack->saveState( dataFile, dataFile.content() );
|
||||
|
||||
QDomElement ge = dataFile.createElement( "groove" );
|
||||
ge.setAttribute("type", m_globalGroove->nodeName());
|
||||
dataFile.content().appendChild( ge );
|
||||
m_globalGroove->saveState( dataFile, ge );
|
||||
|
||||
Engine::mixer()->saveState( dataFile, dataFile.content() );
|
||||
if( getGUI() != nullptr )
|
||||
{
|
||||
@@ -1247,6 +1267,10 @@ bool Song::saveProjectFile(const QString & filename, bool withResources)
|
||||
return dataFile.writeFile(filename, withResources);
|
||||
}
|
||||
|
||||
void Song::setGlobalGroove(Groove * groove)
|
||||
{
|
||||
m_globalGroove = groove;
|
||||
}
|
||||
|
||||
|
||||
// Save the current song
|
||||
|
||||
@@ -107,6 +107,7 @@ SET(LMMS_SRCS
|
||||
gui/widgets/Fader.cpp
|
||||
gui/widgets/FloatModelEditorBase.cpp
|
||||
gui/widgets/Graph.cpp
|
||||
gui/widgets/GrooveView.cpp
|
||||
gui/widgets/GroupBox.cpp
|
||||
gui/widgets/Knob.cpp
|
||||
gui/widgets/LcdFloatSpinBox.cpp
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "CPULoadWidget.h"
|
||||
#include "DeprecationHelper.h"
|
||||
#include "embed.h"
|
||||
#include "GrooveView.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "LcdSpinBox.h"
|
||||
#include "MainWindow.h"
|
||||
@@ -163,6 +164,11 @@ SongEditor::SongEditor( Song * song ) :
|
||||
|
||||
getGUI()->mainWindow()->addSpacingToToolBar( 10 );
|
||||
|
||||
getGUI()->mainWindow()->addWidgetToToolBar( new GrooveView( tb ) );
|
||||
|
||||
getGUI()->mainWindow()->addSpacingToToolBar( 10 );
|
||||
|
||||
|
||||
auto master_vol_lbl = new QLabel(tb);
|
||||
master_vol_lbl->setPixmap( embed::getIconPixmap( "master_volume" ) );
|
||||
|
||||
|
||||
@@ -110,6 +110,18 @@ InstrumentTuningView::InstrumentTuningView(InstrumentTrack *it, QWidget *parent)
|
||||
m_rangeImportCheckbox->setCheckable(true);
|
||||
microtunerLayout->addWidget(m_rangeImportCheckbox);
|
||||
|
||||
// Groove toggle
|
||||
m_grooveGroupBox = new GroupBox(tr("GROOVE"));
|
||||
m_grooveGroupBox->setModel(&it->m_useGrooveModel);
|
||||
layout->addWidget(m_grooveGroupBox);
|
||||
|
||||
QHBoxLayout *grooveLayout = new QHBoxLayout(m_grooveGroupBox);
|
||||
grooveLayout->setContentsMargins(8, 18, 8, 8);
|
||||
|
||||
QLabel *grooveLabel = new QLabel(tr("Enables the use of Groove"));
|
||||
grooveLabel->setFont(adjustedToPixelSize(tlabel->font(), 8));
|
||||
grooveLayout->addWidget(grooveLabel);
|
||||
|
||||
// Fill remaining space
|
||||
layout->addStretch();
|
||||
}
|
||||
|
||||
139
src/gui/widgets/GrooveView.cpp
Normal file
139
src/gui/widgets/GrooveView.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "GrooveView.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "embed.h"
|
||||
#include "Engine.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Song.h"
|
||||
#include "GuiApplication.h"
|
||||
|
||||
#include "Groove.h"
|
||||
#include "HydrogenSwing.h"
|
||||
#include "HalfSwing.h"
|
||||
#include "GrooveExperiments.h"
|
||||
#include "MidiSwing.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
GrooveView::GrooveView(QWidget * parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
m_layout = new QVBoxLayout(this);
|
||||
|
||||
m_comboBox = new QComboBox(this);
|
||||
// Insert reverse order.
|
||||
m_comboBox->insertItem(0, tr("Experiment swing") , QVariant::fromValue(5));
|
||||
m_comboBox->insertItem(0, tr("Half swing") , QVariant::fromValue(4));
|
||||
m_comboBox->insertItem(0, tr("Hydrogen swing") , QVariant::fromValue(3));
|
||||
m_comboBox->insertItem(0, tr("MIDI swing") , QVariant::fromValue(2));
|
||||
m_comboBox->insertItem(0, tr("No swing") , QVariant::fromValue(1));
|
||||
m_comboBox->setCurrentIndex(0);
|
||||
|
||||
m_layout->addWidget(m_comboBox);
|
||||
m_layout->addWidget(new QLabel(""));
|
||||
|
||||
connect(m_comboBox, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(grooveChanged()));
|
||||
|
||||
connect(Engine::getSong(), SIGNAL(dataChanged()),
|
||||
this, SLOT(update()));
|
||||
|
||||
connect(Engine::getSong(), SIGNAL(projectLoaded()),
|
||||
this, SLOT(update()));
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
GrooveView::~GrooveView()
|
||||
{
|
||||
}
|
||||
|
||||
void GrooveView::update()
|
||||
{
|
||||
Groove * groove = Engine::getSong()->globalGroove();
|
||||
if (groove->nodeName() == "none")
|
||||
{
|
||||
m_comboBox->setCurrentIndex(0);
|
||||
}
|
||||
if (groove->nodeName() == "midi")
|
||||
{
|
||||
m_comboBox->setCurrentIndex(1);
|
||||
}
|
||||
if (groove->nodeName() == "hydrogen")
|
||||
{
|
||||
m_comboBox->setCurrentIndex(2);
|
||||
}
|
||||
if (groove->nodeName() == "half")
|
||||
{
|
||||
m_comboBox->setCurrentIndex(3);
|
||||
}
|
||||
if (groove->nodeName() == "experiment")
|
||||
{
|
||||
m_comboBox->setCurrentIndex(4);
|
||||
}
|
||||
setView(groove);
|
||||
}
|
||||
|
||||
void GrooveView::clear()
|
||||
{
|
||||
QLayoutItem * li = m_layout->takeAt(1);
|
||||
delete li->widget();
|
||||
delete li;
|
||||
|
||||
m_comboBox->setCurrentIndex(0);
|
||||
m_layout->addWidget(new QLabel(""));
|
||||
}
|
||||
|
||||
void GrooveView::grooveChanged()
|
||||
{
|
||||
Groove * groove = NULL;
|
||||
|
||||
int currentIndex = m_comboBox->currentIndex();
|
||||
switch (currentIndex) {
|
||||
case 0 :
|
||||
{
|
||||
groove = new Groove();
|
||||
break;
|
||||
}
|
||||
case 1 :
|
||||
{
|
||||
groove = new MidiSwing();
|
||||
break;
|
||||
}
|
||||
case 2 :
|
||||
{
|
||||
groove = new HydrogenSwing();
|
||||
break;
|
||||
}
|
||||
case 3 :
|
||||
{
|
||||
groove = new HalfSwing();
|
||||
break;
|
||||
}
|
||||
case 4 :
|
||||
{
|
||||
groove = new GrooveExperiments();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Song * song = Engine::getSong();
|
||||
song->setGlobalGroove(groove); // TODO: This can fail.
|
||||
setView(groove);
|
||||
}
|
||||
|
||||
void GrooveView::setView(Groove * groove)
|
||||
{
|
||||
QWidget * view = groove->instantiateView(this);
|
||||
QLayoutItem * li = m_layout->takeAt(1);
|
||||
delete li->widget();
|
||||
delete li;
|
||||
|
||||
m_layout->addWidget(view);
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
@@ -62,10 +62,13 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr( "Volume" ) ),
|
||||
m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ),
|
||||
m_audioPort( tr( "unnamed_track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ),
|
||||
m_groove( nullptr ),
|
||||
m_noGroove( nullptr ),
|
||||
m_pitchModel( 0, MinPitchDefault, MaxPitchDefault, 1, this, tr( "Pitch" ) ),
|
||||
m_pitchRangeModel( 1, 1, 60, this, tr( "Pitch range" ) ),
|
||||
m_mixerChannelModel( 0, 0, 0, this, tr( "Mixer channel" ) ),
|
||||
m_useMasterPitchModel( true, this, tr( "Master pitch") ),
|
||||
m_useGrooveModel( true, this, tr( "Groove" ) ),
|
||||
m_instrument( nullptr ),
|
||||
m_soundShaping( this ),
|
||||
m_arpeggio( this ),
|
||||
@@ -110,6 +113,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
connect(&m_pitchModel, SIGNAL(dataChanged()), this, SLOT(updatePitch()), Qt::DirectConnection);
|
||||
connect(&m_pitchRangeModel, SIGNAL(dataChanged()), this, SLOT(updatePitchRange()), Qt::DirectConnection);
|
||||
connect(&m_mixerChannelModel, SIGNAL(dataChanged()), this, SLOT(updateMixerChannel()), Qt::DirectConnection);
|
||||
connect(&m_useGrooveModel, SIGNAL(dataChanged()), this, SLOT(updateGroove()), Qt::DirectConnection );
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +219,10 @@ InstrumentTrack::~InstrumentTrack()
|
||||
|
||||
// now we're save deleting the instrument
|
||||
if( m_instrument ) delete m_instrument;
|
||||
|
||||
if (m_noGroove != nullptr) {
|
||||
delete m_noGroove;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -737,6 +745,10 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
return false;
|
||||
}
|
||||
|
||||
// pluggable algorithm for playing notes that are
|
||||
// posated within the current sample-frame
|
||||
Groove * globalGroove = Engine::getSong()->globalGroove();
|
||||
|
||||
bool played_a_note = false; // will be return variable
|
||||
|
||||
for (const auto& clip : clips)
|
||||
@@ -759,10 +771,19 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
// ...and set our index to zero
|
||||
auto nit = notes.begin();
|
||||
|
||||
Groove * groove = this->groove();
|
||||
if (!groove) {
|
||||
groove = globalGroove;
|
||||
}
|
||||
|
||||
// very effective algorithm for playing notes that are
|
||||
// posated within the current sample-frame
|
||||
|
||||
|
||||
|
||||
// FIXME: Uncomment once groove is straight.
|
||||
// Perhaps have groove supply max shift so we can skip some notes if not all
|
||||
/*
|
||||
if( cur_start > 0 )
|
||||
{
|
||||
// skip notes which are posated before start-bar
|
||||
@@ -771,29 +792,32 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
++nit;
|
||||
}
|
||||
}
|
||||
|
||||
while (nit != notes.end() && (*nit)->pos() == cur_start)
|
||||
*/
|
||||
while (nit != notes.end())
|
||||
{
|
||||
const auto currentNote = *nit;
|
||||
|
||||
// If the note is a Step Note, frames will be 0 so the NotePlayHandle
|
||||
// plays for the whole length of the sample
|
||||
const auto noteFrames = currentNote->type() == Note::Type::Step
|
||||
? 0
|
||||
: currentNote->length().frames(frames_per_tick);
|
||||
|
||||
NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire(this, _offset, noteFrames, *currentNote);
|
||||
notePlayHandle->setPatternTrack(pattern_track);
|
||||
// are we playing global song?
|
||||
if( _clip_num < 0 )
|
||||
const auto grooveOffset = groove->isInTick(&cur_start, _frames, _offset, currentNote, c);
|
||||
if (grooveOffset >= 0)
|
||||
{
|
||||
// then set song-global offset of clip in order to
|
||||
// properly perform the note detuning
|
||||
notePlayHandle->setSongGlobalParentOffset( c->startPosition() );
|
||||
}
|
||||
// If the note is a Step Note, frames will be 0 so the NotePlayHandle
|
||||
// plays for the whole length of the sample
|
||||
const auto noteFrames = currentNote->type() == Note::Type::Step
|
||||
? 0
|
||||
: currentNote->length().frames(frames_per_tick);
|
||||
|
||||
Engine::audioEngine()->addPlayHandle( notePlayHandle );
|
||||
played_a_note = true;
|
||||
NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire(this, grooveOffset, noteFrames, *currentNote);
|
||||
notePlayHandle->setPatternTrack(pattern_track);
|
||||
// are we playing global song?
|
||||
if ( _clip_num < 0 )
|
||||
{
|
||||
// then set song-global offset of pattern in order to
|
||||
// properly perform the note detuning
|
||||
notePlayHandle->setSongGlobalParentOffset( c->startPosition() );
|
||||
}
|
||||
|
||||
Engine::audioEngine()->addPlayHandle( notePlayHandle );
|
||||
played_a_note = true;
|
||||
}
|
||||
++nit;
|
||||
}
|
||||
}
|
||||
@@ -801,8 +825,37 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
return played_a_note;
|
||||
}
|
||||
|
||||
Groove * InstrumentTrack::groove()
|
||||
{
|
||||
if (m_grooveOn)
|
||||
{
|
||||
// nullptr: Use global groove
|
||||
return m_groove;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable global groove
|
||||
return m_noGroove;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InstrumentTrack::updateGroove()
|
||||
{
|
||||
if (m_useGrooveModel.value())
|
||||
{
|
||||
m_grooveOn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_noGroove)
|
||||
{
|
||||
m_noGroove = new Groove();
|
||||
}
|
||||
m_grooveOn = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Clip* InstrumentTrack::createClip(const TimePos & pos)
|
||||
{
|
||||
@@ -835,6 +888,7 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement
|
||||
m_lastKeyModel.saveSettings(doc, thisElement, "lastkey");
|
||||
m_useMasterPitchModel.saveSettings( doc, thisElement, "usemasterpitch");
|
||||
m_microtuner.saveSettings(doc, thisElement);
|
||||
m_useGrooveModel.saveSettings( doc, thisElement, "usegroove");
|
||||
|
||||
// Save MIDI CC stuff
|
||||
m_midiCCEnable->saveSettings(doc, thisElement, "enablecc");
|
||||
@@ -904,6 +958,7 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement
|
||||
m_lastKeyModel.loadSettings(thisElement, "lastkey");
|
||||
m_useMasterPitchModel.loadSettings( thisElement, "usemasterpitch");
|
||||
m_microtuner.loadSettings(thisElement);
|
||||
m_useGrooveModel.loadSettings(thisElement, "usegroove");
|
||||
|
||||
// clear effect-chain just in case we load an old preset without FX-data
|
||||
m_audioPort.effects()->clear();
|
||||
|
||||
Reference in New Issue
Block a user