diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp
index 55912882d204..4eca26de337d 100644
--- a/src/library/dlgtrackinfo.cpp
+++ b/src/library/dlgtrackinfo.cpp
@@ -63,6 +63,10 @@ void DlgTrackInfo::init() {
this, SLOT(slotBpmTwoThirds()));
connect(bpmThreeFourth, SIGNAL(clicked()),
this, SLOT(slotBpmThreeFourth()));
+ connect(bpmFourThirds, SIGNAL(clicked()),
+ this, SLOT(slotBpmFourThirds()));
+ connect(bpmThreeHalves, SIGNAL(clicked()),
+ this, SLOT(slotBpmThreeHalves()));
connect(bpmClear, SIGNAL(clicked()),
this, SLOT(slotBpmClear()));
@@ -503,6 +507,20 @@ void DlgTrackInfo::slotBpmThreeFourth() {
spinBpm->setValue(newValue);
}
+void DlgTrackInfo::slotBpmFourThirds() {
+ m_pBeatsClone->scale(Beats::FOURTHIRDS);
+ // read back the actual value
+ double newValue = m_pBeatsClone->getBpm();
+ spinBpm->setValue(newValue);
+}
+
+void DlgTrackInfo::slotBpmThreeHalves() {
+ m_pBeatsClone->scale(Beats::THREEHALVES);
+ // read back the actual value
+ double newValue = m_pBeatsClone->getBpm();
+ spinBpm->setValue(newValue);
+}
+
void DlgTrackInfo::slotBpmClear() {
spinBpm->setValue(0);
m_pBeatsClone.clear();
diff --git a/src/library/dlgtrackinfo.h b/src/library/dlgtrackinfo.h
index 51dd1a851f9d..92513218c0ae 100644
--- a/src/library/dlgtrackinfo.h
+++ b/src/library/dlgtrackinfo.h
@@ -47,6 +47,8 @@ class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo {
void slotBpmHalve();
void slotBpmTwoThirds();
void slotBpmThreeFourth();
+ void slotBpmFourThirds();
+ void slotBpmThreeHalves();
void slotBpmClear();
void slotBpmConstChanged(int state);
void slotBpmTap(double averageLength, int numSamples);
diff --git a/src/library/dlgtrackinfo.ui b/src/library/dlgtrackinfo.ui
index ddab8a45ae95..5c039236fc2f 100644
--- a/src/library/dlgtrackinfo.ui
+++ b/src/library/dlgtrackinfo.ui
@@ -794,6 +794,38 @@
+ -
+
+
+
+ 125
+ 0
+
+
+
+ Sets the BPM to 150% of the current value.
+
+
+ 3/2 BPM
+
+
+
+ -
+
+
+
+ 125
+ 0
+
+
+
+ Sets the BPM to 133% of the current value.
+
+
+ 4/3 BPM
+
+
+
-
@@ -814,7 +846,7 @@
123
- 91
+ 128
diff --git a/src/test/beatgridtest.cpp b/src/test/beatgridtest.cpp
index 9ca5da5b9de8..801546b61622 100644
--- a/src/test/beatgridtest.cpp
+++ b/src/test/beatgridtest.cpp
@@ -27,8 +27,14 @@ TEST(BeatGridTest, Scale) {
pGrid->scale(Beats::TWOTHIRDS);
EXPECT_DOUBLE_EQ(bpm * 2 / 3, pGrid->getBpm());
+ pGrid->scale(Beats::THREEHALVES);
+ EXPECT_DOUBLE_EQ(bpm, pGrid->getBpm());
+
pGrid->scale(Beats::THREEFOURTHS);
- EXPECT_DOUBLE_EQ(bpm / 2, pGrid->getBpm());
+ EXPECT_DOUBLE_EQ(bpm * 3 / 4, pGrid->getBpm());
+
+ pGrid->scale(Beats::FOURTHIRDS);
+ EXPECT_DOUBLE_EQ(bpm, pGrid->getBpm());
}
TEST(BeatGridTest, TestNthBeatWhenOnBeat) {
diff --git a/src/test/beatmaptest.cpp b/src/test/beatmaptest.cpp
index 42ff7a0c3849..9fc62a86fd8d 100644
--- a/src/test/beatmaptest.cpp
+++ b/src/test/beatmaptest.cpp
@@ -60,8 +60,14 @@ TEST_F(BeatMapTest, Scale) {
pMap->scale(Beats::TWOTHIRDS);
EXPECT_DOUBLE_EQ(bpm * 2 / 3, pMap->getBpm());
+ pMap->scale(Beats::THREEHALVES);
+ EXPECT_DOUBLE_EQ(bpm, pMap->getBpm());
+
pMap->scale(Beats::THREEFOURTHS);
- EXPECT_DOUBLE_EQ(bpm / 2, pMap->getBpm());
+ EXPECT_DOUBLE_EQ(bpm * 3 / 4, pMap->getBpm());
+
+ pMap->scale(Beats::FOURTHIRDS);
+ EXPECT_DOUBLE_EQ(bpm, pMap->getBpm());
}
TEST_F(BeatMapTest, TestNthBeat) {
diff --git a/src/track/beatgrid.cpp b/src/track/beatgrid.cpp
index 27aed7afad17..ea6ef9dcfe4d 100644
--- a/src/track/beatgrid.cpp
+++ b/src/track/beatgrid.cpp
@@ -358,6 +358,12 @@ void BeatGrid::scale(enum BPMScale scale) {
case THREEFOURTHS:
bpm *= 3.0 / 4;
break;
+ case FOURTHIRDS:
+ bpm *= 4.0 / 3;
+ break;
+ case THREEHALVES:
+ bpm *= 3.0 / 2;
+ break;
default:
DEBUG_ASSERT(!"scale value invalid");
return;
diff --git a/src/track/beatmap.cpp b/src/track/beatmap.cpp
index 41efe5dc7076..7e0b0e74e9e9 100644
--- a/src/track/beatmap.cpp
+++ b/src/track/beatmap.cpp
@@ -552,9 +552,6 @@ void BeatMap::scale(enum BPMScale scale) {
switch (scale) {
case DOUBLE:
- if (getBpm() * 2 > getMaxBpm()) {
- return;
- }
// introduce a new beat into every gap
scaleDouble();
break;
@@ -574,6 +571,18 @@ void BeatMap::scale(enum BPMScale scale) {
// remove every second third and forth beat
scaleFourth();
break;
+ case FOURTHIRDS:
+ // introduce three beats into every gap
+ scaleQuadruple();
+ // remove every second third and forth beat
+ scaleThird();
+ break;
+ case THREEHALVES:
+ // introduce two beats into every gap
+ scaleTriple();
+ // remove every second beat
+ scaleHalve();
+ break;
default:
DEBUG_ASSERT(!"scale value invalid");
return;
@@ -614,6 +623,22 @@ void BeatMap::scaleTriple() {
}
}
+void BeatMap::scaleQuadruple() {
+ Beat prevBeat = m_beats.first();
+ // Skip the first beat to preserve the first beat in a measure
+ BeatList::iterator it = m_beats.begin() + 1;
+ for (; it != m_beats.end(); ++it) {
+ // Need to not accrue fractional frames.
+ int distance = it->frame_position() - prevBeat.frame_position();
+ Beat beat;
+ for (int i = 1; i <= 3; i++) {
+ beat.set_frame_position(prevBeat.frame_position() + distance * i / 4);
+ it = m_beats.insert(it, beat);
+ }
+ prevBeat = (++it)[0];
+ }
+}
+
void BeatMap::scaleHalve() {
// Skip the first beat to preserve the first beat in a measure
BeatList::iterator it = m_beats.begin() + 1;
diff --git a/src/track/beatmap.h b/src/track/beatmap.h
index 580285609f98..42da0d477ae3 100644
--- a/src/track/beatmap.h
+++ b/src/track/beatmap.h
@@ -98,6 +98,7 @@ class BeatMap : public QObject, public Beats {
void scaleDouble();
void scaleTriple();
+ void scaleQuadruple();
void scaleHalve();
void scaleThird();
void scaleFourth();
diff --git a/src/track/beats.h b/src/track/beats.h
index db89cf3abf18..32d167d23556 100644
--- a/src/track/beats.h
+++ b/src/track/beats.h
@@ -45,6 +45,8 @@ class Beats {
HALVE,
TWOTHIRDS,
THREEFOURTHS,
+ FOURTHIRDS,
+ THREEHALVES,
};
virtual Beats::CapabilitiesFlags getCapabilities() const = 0;
diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp
index a5e753e3e872..8f74136f77e0 100644
--- a/src/widget/wtracktableview.cpp
+++ b/src/widget/wtracktableview.cpp
@@ -132,6 +132,8 @@ WTrackTableView::~WTrackTableView() {
delete m_pBpmHalveAction;
delete m_pBpmTwoThirdsAction;
delete m_pBpmThreeFourthsAction;
+ delete m_pBpmFourThirdsAction;
+ delete m_pBpmThreeHalvesAction;
delete m_pBPMMenu;
delete m_pReplayGainResetAction;
delete m_pPurgeAct;
@@ -419,11 +421,15 @@ void WTrackTableView::createActions() {
m_pBpmHalveAction = new QAction(tr("Halve BPM"), this);
m_pBpmTwoThirdsAction = new QAction(tr("2/3 BPM"), this);
m_pBpmThreeFourthsAction = new QAction(tr("3/4 BPM"), this);
+ m_pBpmFourThirdsAction = new QAction(tr("4/3 BPM"), this);
+ m_pBpmThreeHalvesAction = new QAction(tr("3/2 BPM"), this);
m_BpmMapper.setMapping(m_pBpmDoubleAction, Beats::DOUBLE);
m_BpmMapper.setMapping(m_pBpmHalveAction, Beats::HALVE);
m_BpmMapper.setMapping(m_pBpmTwoThirdsAction, Beats::TWOTHIRDS);
m_BpmMapper.setMapping(m_pBpmThreeFourthsAction, Beats::THREEFOURTHS);
+ m_BpmMapper.setMapping(m_pBpmFourThirdsAction, Beats::FOURTHIRDS);
+ m_BpmMapper.setMapping(m_pBpmThreeHalvesAction, Beats::THREEHALVES);
connect(m_pBpmDoubleAction, SIGNAL(triggered()),
&m_BpmMapper, SLOT(map()));
@@ -433,6 +439,10 @@ void WTrackTableView::createActions() {
&m_BpmMapper, SLOT(map()));
connect(m_pBpmThreeFourthsAction, SIGNAL(triggered()),
&m_BpmMapper, SLOT(map()));
+ connect(m_pBpmFourThirdsAction, SIGNAL(triggered()),
+ &m_BpmMapper, SLOT(map()));
+ connect(m_pBpmThreeHalvesAction, SIGNAL(triggered()),
+ &m_BpmMapper, SLOT(map()));
m_pClearBeatsAction = new QAction(tr("Clear BPM and Beatgrid"), this);
connect(m_pClearBeatsAction, SIGNAL(triggered()),
@@ -837,6 +847,8 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
m_pBPMMenu->addAction(m_pBpmHalveAction);
m_pBPMMenu->addAction(m_pBpmTwoThirdsAction);
m_pBPMMenu->addAction(m_pBpmThreeFourthsAction);
+ m_pBPMMenu->addAction(m_pBpmFourThirdsAction);
+ m_pBPMMenu->addAction(m_pBpmThreeHalvesAction);
m_pBPMMenu->addSeparator();
m_pBPMMenu->addAction(m_pBpmLockAction);
m_pBPMMenu->addAction(m_pBpmUnlockAction);
@@ -854,6 +866,8 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
m_pBpmHalveAction->setEnabled(false);
m_pBpmTwoThirdsAction->setEnabled(false);
m_pBpmThreeFourthsAction->setEnabled(false);
+ m_pBpmFourThirdsAction->setEnabled(false);
+ m_pBpmThreeHalvesAction->setEnabled(false);
} else { //BPM is not locked
m_pBpmUnlockAction->setEnabled(false);
m_pBpmLockAction->setEnabled(true);
@@ -861,6 +875,8 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
m_pBpmHalveAction->setEnabled(true);
m_pBpmTwoThirdsAction->setEnabled(true);
m_pBpmThreeFourthsAction->setEnabled(true);
+ m_pBpmFourThirdsAction->setEnabled(true);
+ m_pBpmThreeHalvesAction->setEnabled(true);
}
} else {
bool anyLocked = false; //true if any of the selected items are locked
@@ -878,12 +894,18 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
m_pBpmDoubleAction->setEnabled(false);
m_pBpmHalveAction->setEnabled(false);
m_pBpmTwoThirdsAction->setEnabled(false);
+ m_pBpmThreeFourthsAction->setEnabled(false);
+ m_pBpmFourThirdsAction->setEnabled(false);
+ m_pBpmThreeHalvesAction->setEnabled(false);
} else {
m_pBpmLockAction->setEnabled(true);
m_pBpmUnlockAction->setEnabled(false);
m_pBpmDoubleAction->setEnabled(true);
m_pBpmHalveAction->setEnabled(true);
m_pBpmTwoThirdsAction->setEnabled(true);
+ m_pBpmThreeFourthsAction->setEnabled(true);
+ m_pBpmFourThirdsAction->setEnabled(true);
+ m_pBpmThreeHalvesAction->setEnabled(true);
}
}
}
diff --git a/src/widget/wtracktableview.h b/src/widget/wtracktableview.h
index 5836006ef87c..6e1172960ac4 100644
--- a/src/widget/wtracktableview.h
+++ b/src/widget/wtracktableview.h
@@ -151,6 +151,8 @@ class WTrackTableView : public WLibraryTableView {
QAction *m_pBpmHalveAction;
QAction *m_pBpmTwoThirdsAction;
QAction *m_pBpmThreeFourthsAction;
+ QAction *m_pBpmFourThirdsAction;
+ QAction *m_pBpmThreeHalvesAction;
// Clear track beats
QAction* m_pClearBeatsAction;