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;