Skip to content

Commit

Permalink
Beats: Use frame positions for beatmap/beatgrid construction
Browse files Browse the repository at this point in the history
  • Loading branch information
Holzhaus committed Jul 1, 2021
1 parent 0321589 commit c70d208
Show file tree
Hide file tree
Showing 25 changed files with 470 additions and 297 deletions.
4 changes: 2 additions & 2 deletions src/analyzer/analyzerbeats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ void AnalyzerBeats::storeResults(TrackPointer pTrack) {

mixxx::BeatsPointer pBeats;
if (m_pPlugin->supportsBeatTracking()) {
QVector<double> beats = m_pPlugin->getBeats();
QVector<mixxx::audio::FramePos> beats = m_pPlugin->getBeats();
QHash<QString, QString> extraVersionInfo = getExtraVersionInfo(
m_pluginId, m_bPreferencesFastAnalysis);
pBeats = BeatFactory::makePreferredBeats(
Expand All @@ -232,7 +232,7 @@ void AnalyzerBeats::storeResults(TrackPointer pTrack) {
} else {
float bpm = m_pPlugin->getBpm();
qDebug() << "AnalyzerBeats plugin detected constant BPM: " << bpm;
pBeats = BeatFactory::makeBeatGrid(m_sampleRate, bpm, 0.0f);
pBeats = BeatFactory::makeBeatGrid(m_sampleRate, bpm, mixxx::audio::kStartFramePos);
}

pTrack->trySetBeats(pBeats);
Expand Down
5 changes: 3 additions & 2 deletions src/analyzer/plugins/analyzerplugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QString>

#include "audio/frame.h"
#include "track/beats.h"
#include "track/keys.h"
#include "util/types.h"
Expand Down Expand Up @@ -71,8 +72,8 @@ class AnalyzerBeatsPlugin : public AnalyzerPlugin {
virtual float getBpm() const {
return 0.0f;
}
virtual QVector<double> getBeats() const {
return QVector<double>();
virtual QVector<mixxx::audio::FramePos> getBeats() const {
return {};
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/analyzer/plugins/analyzerqueenmarybeats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ bool AnalyzerQueenMaryBeats::finalize() {
for (size_t i = 0; i < beats.size(); ++i) {
// we add the halve m_stepSize here, because the beat
// is detected between the two samples.
double result = (beats.at(i) * m_stepSize) + m_stepSize / 2;
const auto result = mixxx::audio::FramePos((beats.at(i) * m_stepSize) + m_stepSize / 2);
m_resultBeats.push_back(result);
}

Expand Down
4 changes: 2 additions & 2 deletions src/analyzer/plugins/analyzerqueenmarybeats.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class AnalyzerQueenMaryBeats : public AnalyzerBeatsPlugin {
return true;
}

QVector<double> getBeats() const override {
QVector<mixxx::audio::FramePos> getBeats() const override {
return m_resultBeats;
}

Expand All @@ -51,7 +51,7 @@ class AnalyzerQueenMaryBeats : public AnalyzerBeatsPlugin {
int m_windowSize;
int m_stepSize;
std::vector<double> m_detectionResults;
QVector<double> m_resultBeats;
QVector<mixxx::audio::FramePos> m_resultBeats;
};

} // namespace mixxx
3 changes: 2 additions & 1 deletion src/library/dao/trackdao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,8 @@ bool setTrackBeats(const QSqlRecord& record, const int column,
}
} else {
// Load a temorary beat grid without offset that will be replaced by the analyzer.
const auto pBeats = BeatFactory::makeBeatGrid(pTrack->getSampleRate(), bpm, 0.0);
const auto pBeats = BeatFactory::makeBeatGrid(
pTrack->getSampleRate(), bpm, mixxx::audio::kStartFramePos);
pTrack->trySetBeats(pBeats);
}
return false;
Expand Down
11 changes: 8 additions & 3 deletions src/library/dlgtrackinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,11 @@ void DlgTrackInfo::slotBpmConstChanged(int state) {
// The cue point should be set on a beat, so this seems
// to be a good alternative
CuePosition cue = m_pLoadedTrack->getCuePoint();
m_pBeatsClone = BeatFactory::makeBeatGrid(
m_pLoadedTrack->getSampleRate(), spinBpm->value(), cue.getPosition());
m_pBeatsClone =
BeatFactory::makeBeatGrid(m_pLoadedTrack->getSampleRate(),
spinBpm->value(),
mixxx::audio::FramePos::fromEngineSamplePos(
cue.getPosition()));
} else {
m_pBeatsClone.clear();
}
Expand Down Expand Up @@ -618,7 +621,9 @@ void DlgTrackInfo::slotSpinBpmValueChanged(double value) {
if (!m_pBeatsClone) {
CuePosition cue = m_pLoadedTrack->getCuePoint();
m_pBeatsClone = BeatFactory::makeBeatGrid(
m_pLoadedTrack->getSampleRate(), value, cue.getPosition());
m_pLoadedTrack->getSampleRate(),
value,
mixxx::audio::FramePos::fromEngineSamplePos(cue.getPosition()));
}

double oldValue = m_pBeatsClone->getBpm();
Expand Down
4 changes: 2 additions & 2 deletions src/library/rekordbox/rekordboxfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ void readAnalyze(TrackPointer track,
static_cast<rekordbox_anlz_t::beat_grid_tag_t*>(
(*section)->body());

QVector<double> beats;
QVector<mixxx::audio::FramePos> beats;

for (std::vector<rekordbox_anlz_t::beat_grid_beat_t*>::iterator
beat = beatGridTag->beats()->begin();
Expand All @@ -936,7 +936,7 @@ void readAnalyze(TrackPointer track,
if (time < 1) {
time = 1;
}
beats << (sampleRateKhz * static_cast<double>(time));
beats << mixxx::audio::FramePos(sampleRateKhz * static_cast<double>(time));
}

const auto pBeats = mixxx::BeatMap::makeBeatMap(
Expand Down
25 changes: 20 additions & 5 deletions src/test/beatgridtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ TEST(BeatGridTest, Scale) {
double bpm = 60.0;
pTrack->trySetBpm(bpm);

auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), QString(), bpm, 0);
auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(),
QString(),
bpm,
mixxx::audio::kStartFramePos);

EXPECT_DOUBLE_EQ(bpm, pGrid->getBpm());
pGrid = pGrid->scale(Beats::DOUBLE);
Expand Down Expand Up @@ -60,7 +63,10 @@ TEST(BeatGridTest, TestNthBeatWhenOnBeat) {
pTrack->trySetBpm(bpm);
double beatLength = (60.0 * sampleRate / bpm) * kFrameSize;

auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), QString(), bpm, 0);
auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(),
QString(),
bpm,
mixxx::audio::kStartFramePos);
// Pretend we're on the 20th beat;
double position = beatLength * 20;

Expand Down Expand Up @@ -99,7 +105,10 @@ TEST(BeatGridTest, TestNthBeatWhenOnBeat_BeforeEpsilon) {
pTrack->trySetBpm(bpm);
double beatLength = (60.0 * sampleRate / bpm) * kFrameSize;

auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), QString(), bpm, 0);
auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(),
QString(),
bpm,
mixxx::audio::kStartFramePos);

// Pretend we're just before the 20th beat.
const double kClosestBeat = 20 * beatLength;
Expand Down Expand Up @@ -140,7 +149,10 @@ TEST(BeatGridTest, TestNthBeatWhenOnBeat_AfterEpsilon) {
pTrack->trySetBpm(bpm);
double beatLength = (60.0 * sampleRate / bpm) * kFrameSize;

auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), QString(), bpm, 0);
auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(),
QString(),
bpm,
mixxx::audio::kStartFramePos);

// Pretend we're just before the 20th beat.
const double kClosestBeat = 20 * beatLength;
Expand Down Expand Up @@ -181,7 +193,10 @@ TEST(BeatGridTest, TestNthBeatWhenNotOnBeat) {
pTrack->trySetBpm(bpm);
double beatLength = (60.0 * sampleRate / bpm) * kFrameSize;

auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), QString(), bpm, 0);
auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(),
QString(),
bpm,
mixxx::audio::kStartFramePos);

// Pretend we're half way between the 20th and 21st beat
double previousBeat = beatLength * 20.0;
Expand Down
71 changes: 39 additions & 32 deletions src/test/beatmaptest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ class BeatMapTest : public testing::Test {
mixxx::Duration::fromSeconds(180));
}

double getBeatLengthFrames(double bpm) {
mixxx::audio::FrameDiff_t getBeatLengthFrames(double bpm) {
return (60.0 * m_iSampleRate / bpm);
}

double getBeatLengthSamples(double bpm) {
return getBeatLengthFrames(bpm) * m_iFrameSize;
}

QVector<double> createBeatVector(double first_beat,
unsigned int num_beats,
double beat_length) {
QVector<double> beats;
QVector<mixxx::audio::FramePos> createBeatVector(mixxx::audio::FramePos first_beat,
unsigned int num_beats,
mixxx::audio::FrameDiff_t beat_length) {
QVector<mixxx::audio::FramePos> beats;
for (unsigned int i = 0; i < num_beats; ++i) {
beats.append(first_beat + i * beat_length);
}
Expand All @@ -49,11 +49,12 @@ class BeatMapTest : public testing::Test {
TEST_F(BeatMapTest, Scale) {
const double bpm = 60.0;
m_pTrack->trySetBpm(bpm);
double beatLengthFrames = getBeatLengthFrames(bpm);
double startOffsetFrames = 7;
mixxx::audio::FrameDiff_t beatLengthFrames = getBeatLengthFrames(bpm);
const auto startOffsetFrames = mixxx::audio::FramePos(7);
const int numBeats = 100;
// Note beats must be in frames, not samples.
QVector<double> beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
QVector<mixxx::audio::FramePos> beats =
createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);

EXPECT_DOUBLE_EQ(bpm, pMap->getBpm());
Expand All @@ -79,13 +80,14 @@ TEST_F(BeatMapTest, Scale) {
TEST_F(BeatMapTest, TestNthBeat) {
const double bpm = 60.0;
m_pTrack->trySetBpm(bpm);
double beatLengthFrames = getBeatLengthFrames(bpm);
double startOffsetFrames = 7;
mixxx::audio::FrameDiff_t beatLengthFrames = getBeatLengthFrames(bpm);
const auto startOffsetFrames = mixxx::audio::FramePos(7);
double beatLengthSamples = getBeatLengthSamples(bpm);
double startOffsetSamples = startOffsetFrames * 2;
const double startOffsetSamples = startOffsetFrames.toEngineSamplePos();
const int numBeats = 100;
// Note beats must be in frames, not samples.
QVector<double> beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
QVector<mixxx::audio::FramePos> beats =
createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);

// Check edge cases
Expand All @@ -111,13 +113,14 @@ TEST_F(BeatMapTest, TestNthBeat) {
TEST_F(BeatMapTest, TestNthBeatWhenOnBeat) {
const double bpm = 60.0;
m_pTrack->trySetBpm(bpm);
double beatLengthFrames = getBeatLengthFrames(bpm);
double startOffsetFrames = 7;
mixxx::audio::FrameDiff_t beatLengthFrames = getBeatLengthFrames(bpm);
const auto startOffsetFrames = mixxx::audio::FramePos(7);
double beatLengthSamples = getBeatLengthSamples(bpm);
double startOffsetSamples = startOffsetFrames * 2;
const double startOffsetSamples = startOffsetFrames.toEngineSamplePos();
const int numBeats = 100;
// Note beats must be in frames, not samples.
QVector<double> beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
QVector<mixxx::audio::FramePos> beats =
createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);

// Pretend we're on the 20th beat;
Expand Down Expand Up @@ -153,13 +156,14 @@ TEST_F(BeatMapTest, TestNthBeatWhenOnBeat) {
TEST_F(BeatMapTest, TestNthBeatWhenOnBeat_BeforeEpsilon) {
const double bpm = 60.0;
m_pTrack->trySetBpm(bpm);
double beatLengthFrames = getBeatLengthFrames(bpm);
double startOffsetFrames = 7;
mixxx::audio::FrameDiff_t beatLengthFrames = getBeatLengthFrames(bpm);
const auto startOffsetFrames = mixxx::audio::FramePos(7);
double beatLengthSamples = getBeatLengthSamples(bpm);
double startOffsetSamples = startOffsetFrames * 2;
const double startOffsetSamples = startOffsetFrames.toEngineSamplePos();
const int numBeats = 100;
// Note beats must be in frames, not samples.
QVector<double> beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
QVector<mixxx::audio::FramePos> beats =
createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);

// Pretend we're just before the 20th beat;
Expand Down Expand Up @@ -197,13 +201,14 @@ TEST_F(BeatMapTest, TestNthBeatWhenOnBeat_BeforeEpsilon) {
TEST_F(BeatMapTest, TestNthBeatWhenOnBeat_AfterEpsilon) {
const double bpm = 60.0;
m_pTrack->trySetBpm(bpm);
double beatLengthFrames = getBeatLengthFrames(bpm);
double startOffsetFrames = 7;
mixxx::audio::FrameDiff_t beatLengthFrames = getBeatLengthFrames(bpm);
const auto startOffsetFrames = mixxx::audio::FramePos(7);
double beatLengthSamples = getBeatLengthSamples(bpm);
double startOffsetSamples = startOffsetFrames * 2;
const double startOffsetSamples = startOffsetFrames.toEngineSamplePos();
const int numBeats = 100;
// Note beats must be in frames, not samples.
QVector<double> beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
QVector<mixxx::audio::FramePos> beats =
createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);

// Pretend we're just after the 20th beat;
Expand Down Expand Up @@ -242,13 +247,14 @@ TEST_F(BeatMapTest, TestNthBeatWhenOnBeat_AfterEpsilon) {
TEST_F(BeatMapTest, TestNthBeatWhenNotOnBeat) {
const double bpm = 60.0;
m_pTrack->trySetBpm(bpm);
double beatLengthFrames = getBeatLengthFrames(bpm);
double startOffsetFrames = 7;
mixxx::audio::FrameDiff_t beatLengthFrames = getBeatLengthFrames(bpm);
const auto startOffsetFrames = mixxx::audio::FramePos(7);
double beatLengthSamples = getBeatLengthSamples(bpm);
double startOffsetSamples = startOffsetFrames * 2;
const double startOffsetSamples = startOffsetFrames.toEngineSamplePos();
const int numBeats = 100;
// Note beats must be in frames, not samples.
QVector<double> beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
QVector<mixxx::audio::FramePos> beats =
createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);

// Pretend we're half way between the 20th and 21st beat
Expand Down Expand Up @@ -286,10 +292,10 @@ TEST_F(BeatMapTest, TestBpmAround) {
m_pTrack->trySetBpm(filebpm);
const int numBeats = 64;

QVector<double> beats;
double beat_pos = 0;
QVector<mixxx::audio::FramePos> beats;
mixxx::audio::FramePos beat_pos = mixxx::audio::kStartFramePos;
for (unsigned int i = 0, bpm=60; i < numBeats; ++i, ++bpm) {
double beat_length = getBeatLengthFrames(bpm);
const mixxx::audio::FrameDiff_t beat_length = getBeatLengthFrames(bpm);
beats.append(beat_pos);
beat_pos += beat_length;
}
Expand All @@ -309,7 +315,8 @@ TEST_F(BeatMapTest, TestBpmAround) {
pMap->getBpmAroundPosition(65 * approx_beat_length, 4));

// Try a really, really short track
beats = createBeatVector(10, 3, getBeatLengthFrames(filebpm));
constexpr auto startFramePos = mixxx::audio::FramePos(10);
beats = createBeatVector(startFramePos, 3, getBeatLengthFrames(filebpm));
pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats);
EXPECT_DOUBLE_EQ(filebpm, pMap->getBpmAroundPosition(1 * approx_beat_length, 4));
}
Expand Down
6 changes: 3 additions & 3 deletions src/test/beatstranslatetest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ class BeatsTranslateTest : public MockedEngineBackendTest {
TEST_F(BeatsTranslateTest, SimpleTranslateMatch) {
// Set up BeatGrids for decks 1 and 2.
const double bpm = 60.0;
const double firstBeat = 0.0;
constexpr auto firstBeat = mixxx::audio::kStartFramePos;
auto grid1 = mixxx::BeatGrid::makeBeatGrid(
m_pTrack1->getSampleRate(), QString(), bpm, firstBeat);
m_pTrack1->trySetBeats(grid1);
ASSERT_DOUBLE_EQ(firstBeat, grid1->findClosestBeat(0));
ASSERT_DOUBLE_EQ(firstBeat.value(), grid1->findClosestBeat(0));

auto grid2 = mixxx::BeatGrid::makeBeatGrid(
m_pTrack2->getSampleRate(), QString(), bpm, firstBeat);
m_pTrack2->trySetBeats(grid2);
ASSERT_DOUBLE_EQ(firstBeat, grid2->findClosestBeat(0));
ASSERT_DOUBLE_EQ(firstBeat.value(), grid2->findClosestBeat(0));

// Seek deck 1 forward a bit.
const double delta = 2222.0;
Expand Down
3 changes: 2 additions & 1 deletion src/test/bpmcontrol_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ TEST_F(BpmControlTest, BeatContext_BeatGrid) {
const int kFrameSize = 2;
const double expectedBeatLength = (60.0 * sampleRate / bpm) * kFrameSize;

const mixxx::BeatsPointer pBeats = BeatFactory::makeBeatGrid(pTrack->getSampleRate(), bpm, 0);
const mixxx::BeatsPointer pBeats = BeatFactory::makeBeatGrid(
pTrack->getSampleRate(), bpm, mixxx::audio::kStartFramePos);

// On a beat.
double prevBeat, nextBeat, beatLength, beatPercentage;
Expand Down
Loading

0 comments on commit c70d208

Please sign in to comment.