Skip to content

Commit

Permalink
Merge pull request #4043 from Holzhaus/frames
Browse files Browse the repository at this point in the history
Add FramePos type and use it for beatgrid creation
  • Loading branch information
daschuer committed Jul 2, 2021
2 parents 5651d71 + a555ae4 commit 5dc50d1
Show file tree
Hide file tree
Showing 29 changed files with 676 additions and 310 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,7 @@ add_executable(mixxx-test
src/test/enginemicrophonetest.cpp
src/test/enginesynctest.cpp
src/test/fileinfo_test.cpp
src/test/frametest.cpp
src/test/globaltrackcache_test.cpp
src/test/hotcuecontrol_test.cpp
src/test/imageutils_test.cpp
Expand Down
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
21 changes: 11 additions & 10 deletions src/analyzer/plugins/analyzerqueenmarybeats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ constexpr float kStepSecs = 0.01161f;
// results in 43 Hz @ 44.1 kHz / 47 Hz @ 48 kHz / 47 Hz @ 96 kHz
constexpr int kMaximumBinSizeHz = 50; // Hz

DFConfig makeDetectionFunctionConfig(int stepSize, int windowSize) {
DFConfig makeDetectionFunctionConfig(int stepSizeFrames, int windowSize) {
// These are the defaults for the VAMP beat tracker plugin we used in Mixxx
// 2.0.
DFConfig config;
config.DFType = DF_COMPLEXSD;
config.stepSize = stepSize;
config.stepSize = stepSizeFrames;
config.frameLength = windowSize;
config.dbRise = 3;
config.adaptiveWhitening = false;
Expand All @@ -41,7 +41,7 @@ DFConfig makeDetectionFunctionConfig(int stepSize, int windowSize) {
AnalyzerQueenMaryBeats::AnalyzerQueenMaryBeats()
: m_iSampleRate(0),
m_windowSize(0),
m_stepSize(0) {
m_stepSizeFrames(0) {
}

AnalyzerQueenMaryBeats::~AnalyzerQueenMaryBeats() {
Expand All @@ -50,14 +50,14 @@ AnalyzerQueenMaryBeats::~AnalyzerQueenMaryBeats() {
bool AnalyzerQueenMaryBeats::initialize(int samplerate) {
m_detectionResults.clear();
m_iSampleRate = samplerate;
m_stepSize = static_cast<int>(m_iSampleRate * kStepSecs);
m_stepSizeFrames = static_cast<int>(m_iSampleRate * kStepSecs);
m_windowSize = MathUtilities::nextPowerOfTwo(m_iSampleRate / kMaximumBinSizeHz);
m_pDetectionFunction = std::make_unique<DetectionFunction>(
makeDetectionFunctionConfig(m_stepSize, m_windowSize));
qDebug() << "input sample rate is " << m_iSampleRate << ", step size is " << m_stepSize;
makeDetectionFunctionConfig(m_stepSizeFrames, m_windowSize));
qDebug() << "input sample rate is " << m_iSampleRate << ", step size is " << m_stepSizeFrames;

m_helper.initialize(
m_windowSize, m_stepSize, [this](double* pWindow, size_t) {
m_windowSize, m_stepSizeFrames, [this](double* pWindow, size_t) {
// TODO(rryan) reserve?
m_detectionResults.push_back(
m_pDetectionFunction->processTimeDomain(pWindow));
Expand Down Expand Up @@ -97,17 +97,18 @@ bool AnalyzerQueenMaryBeats::finalize() {
beatPeriod.push_back(0.0);
}

TempoTrackV2 tt(m_iSampleRate, m_stepSize);
TempoTrackV2 tt(m_iSampleRate, m_stepSizeFrames);
tt.calculateBeatPeriod(df, beatPeriod, tempi);

std::vector<double> beats;
tt.calculateBeats(df, beatPeriod, beats);

m_resultBeats.reserve(static_cast<int>(beats.size()));
for (size_t i = 0; i < beats.size(); ++i) {
// we add the halve m_stepSize here, because the beat
// we add the halve m_stepSizeFrames 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_stepSizeFrames) + m_stepSizeFrames / 2);
m_resultBeats.push_back(result);
}

Expand Down
6 changes: 3 additions & 3 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 @@ -49,9 +49,9 @@ class AnalyzerQueenMaryBeats : public AnalyzerBeatsPlugin {
DownmixAndOverlapHelper m_helper;
int m_iSampleRate;
int m_windowSize;
int m_stepSize;
int m_stepSizeFrames;
std::vector<double> m_detectionResults;
QVector<double> m_resultBeats;
QVector<mixxx::audio::FramePos> m_resultBeats;
};

} // namespace mixxx
150 changes: 150 additions & 0 deletions src/audio/frame.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#pragma once

#include <QDebug>
#include <cmath>
#include <limits>

#include "engine/engine.h"
#include "util/fpclassify.h"

namespace mixxx {
namespace audio {
/// FrameDiff_t can be used to store the difference in position between
/// two frames and to store the length of a segment of track in terms of frames.
typedef double FrameDiff_t;

/// FramePos defines the position of a frame in a track
/// with respect to a fixed origin, i.e. start of the track.
class FramePos final {
public:
typedef double value_t;
static constexpr value_t kStartValue = 0;
static constexpr value_t kInvalidValue = std::numeric_limits<FramePos::value_t>::quiet_NaN();

constexpr FramePos()
: m_framePosition(kInvalidValue) {
}

constexpr explicit FramePos(value_t framePosition)
: m_framePosition(framePosition) {
}

static constexpr FramePos fromEngineSamplePos(double engineSamplePos) {
return FramePos(engineSamplePos / mixxx::kEngineChannelCount);
}

constexpr double toEngineSamplePos() const {
return value() * mixxx::kEngineChannelCount;
}

bool isValid() const {
return !util_isnan(m_framePosition) && !util_isinf(m_framePosition);
}

void setValue(value_t framePosition) {
m_framePosition = framePosition;
}

constexpr value_t value() const {
return m_framePosition;
}

bool isFractional() const {
DEBUG_ASSERT(isValid());
value_t integerPart;
return std::modf(value(), &integerPart) != 0;
}

/// Return position rounded to the next lower full frame position, without
/// the fractional part.
[[nodiscard]] FramePos toLowerFrameBoundary() const {
return FramePos(std::floor(value()));
}

FramePos& operator+=(FrameDiff_t increment) {
m_framePosition += increment;
return *this;
}

FramePos& operator-=(FrameDiff_t decrement) {
m_framePosition -= decrement;
return *this;
}

FramePos& operator*=(double multiple) {
m_framePosition *= multiple;
return *this;
}

FramePos& operator/=(double divisor) {
m_framePosition /= divisor;
return *this;
}

private:
value_t m_framePosition;
};

/// FramePos can be added to a FrameDiff_t
inline FramePos operator+(FramePos framePos, FrameDiff_t frameDiff) {
return FramePos(framePos.value() + frameDiff);
}

/// FramePos can be subtracted from a FrameDiff_t
inline FramePos operator-(FramePos framePos, FrameDiff_t frameDiff) {
return FramePos(framePos.value() - frameDiff);
}

/// Two FramePos can be subtracted to get a FrameDiff_t
inline FrameDiff_t operator-(FramePos framePos1, FramePos framePos2) {
return framePos1.value() - framePos2.value();
}

// Adding two FramePos is not allowed since every FramePos shares a common
// reference or origin i.e. the start of the track.

/// FramePos can be multiplied or divided by a double
inline FramePos operator*(FramePos framePos, double multiple) {
return FramePos(framePos.value() * multiple);
}

inline FramePos operator/(FramePos framePos, double divisor) {
return FramePos(framePos.value() / divisor);
}

inline bool operator<(FramePos frame1, FramePos frame2) {
return frame1.value() < frame2.value();
}

inline bool operator<=(FramePos frame1, FramePos frame2) {
return frame1.value() <= frame2.value();
}

inline bool operator>(FramePos frame1, FramePos frame2) {
return frame1.value() > frame2.value();
}

inline bool operator>=(FramePos frame1, FramePos frame2) {
return frame1.value() >= frame2.value();
}

inline bool operator==(FramePos frame1, FramePos frame2) {
return frame1.value() == frame2.value();
}

inline bool operator!=(FramePos frame1, FramePos frame2) {
return !(frame1.value() == frame2.value());
}

inline QDebug operator<<(QDebug dbg, FramePos arg) {
dbg << arg.value();
return dbg;
}

constexpr FramePos kInvalidFramePos = FramePos(FramePos::kInvalidValue);
constexpr FramePos kStartFramePos = FramePos(FramePos::kStartValue);
} // namespace audio
} // namespace mixxx

Q_DECLARE_TYPEINFO(mixxx::audio::FramePos, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(mixxx::audio::FramePos);
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
2 changes: 2 additions & 0 deletions src/mixxxapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <QTouchEvent>
#include <QtDebug>

#include "audio/frame.h"
#include "audio/types.h"
#include "control/controlproxy.h"
#include "library/trackset/crate/crateid.h"
Expand Down Expand Up @@ -97,6 +98,7 @@ void MixxxApplication::registerMetaTypes() {
qRegisterMetaType<mixxx::cache_key_t>("mixxx::cache_key_t");
qRegisterMetaType<mixxx::Bpm>("mixxx::Bpm");
qRegisterMetaType<mixxx::Duration>("mixxx::Duration");
qRegisterMetaType<mixxx::audio::FramePos>("mixxx::audio::FramePos");
qRegisterMetaType<std::optional<mixxx::RgbColor>>("std::optional<mixxx::RgbColor>");
qRegisterMetaType<mixxx::FileInfo>("mixxx::FileInfo");
}
Expand Down
Loading

0 comments on commit 5dc50d1

Please sign in to comment.