1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
#include "track/serato/beatsimporter.h"
#include "track/serato/tags.h"
namespace mixxx {
SeratoBeatsImporter::SeratoBeatsImporter()
: BeatsImporter(),
m_pTerminalMarker(nullptr) {
}
SeratoBeatsImporter::SeratoBeatsImporter(
const QList<SeratoBeatGridNonTerminalMarkerPointer>& nonTerminalMarkers,
SeratoBeatGridTerminalMarkerPointer pTerminalMarker)
: BeatsImporter(),
m_nonTerminalMarkers(nonTerminalMarkers),
m_pTerminalMarker(pTerminalMarker) {
DEBUG_ASSERT(pTerminalMarker);
}
bool SeratoBeatsImporter::isEmpty() const {
return !m_pTerminalMarker;
};
QVector<mixxx::audio::FramePos> SeratoBeatsImporter::importBeatsAndApplyTimingOffset(
const QString& filePath, const audio::StreamInfo& streamInfo) {
const audio::SignalInfo& signalInfo = streamInfo.getSignalInfo();
const double timingOffsetMillis = SeratoTags::guessTimingOffsetMillis(
filePath, signalInfo);
return importBeatsAndApplyTimingOffset(timingOffsetMillis, signalInfo);
}
QVector<mixxx::audio::FramePos> SeratoBeatsImporter::importBeatsAndApplyTimingOffset(
double timingOffsetMillis, const audio::SignalInfo& signalInfo) {
VERIFY_OR_DEBUG_ASSERT(!isEmpty()) {
return {};
}
QVector<mixxx::audio::FramePos> beats;
double beatPositionMillis = 0;
// Calculate beat positions for non-terminal markers
for (int i = 0; i < m_nonTerminalMarkers.size(); ++i) {
SeratoBeatGridNonTerminalMarkerPointer pMarker = m_nonTerminalMarkers.at(i);
beatPositionMillis = static_cast<double>(pMarker->positionSecs()) * 1000;
VERIFY_OR_DEBUG_ASSERT(pMarker->positionSecs() >= 0 &&
pMarker->beatsTillNextMarker() > 0) {
return {};
}
const double nextBeatPositionMillis =
static_cast<double>(i == (m_nonTerminalMarkers.size() - 1)
? m_pTerminalMarker->positionSecs()
: m_nonTerminalMarkers.at(i + 1)
->positionSecs()) *
1000;
VERIFY_OR_DEBUG_ASSERT(nextBeatPositionMillis > beatPositionMillis) {
return {};
}
const double beatLengthMillis =
(nextBeatPositionMillis - beatPositionMillis) /
pMarker->beatsTillNextMarker();
beats.reserve(beats.size() + pMarker->beatsTillNextMarker());
for (quint32 j = 0; j < pMarker->beatsTillNextMarker(); ++j) {
const auto beatPosition = mixxx::audio::FramePos(signalInfo.millis2frames(
beatPositionMillis + timingOffsetMillis));
beats.append(beatPosition.toNearestFrameBoundary());
beatPositionMillis += beatLengthMillis;
}
}
// Calculate remaining beat positions between the last non-terminal marker
// beat (or the start of the track if none exist) and the terminal marker,
// using the given BPM.
//
// beatPositionMillis Terminal Marker
// v v
// | | | | | | | | |[###### Remaining range ######]
const double beatLengthMillis = 60000.0 / static_cast<double>(m_pTerminalMarker->bpm());
VERIFY_OR_DEBUG_ASSERT(m_pTerminalMarker->positionSecs() >= 0 && beatLengthMillis > 0) {
return {};
}
const double rangeEndBeatPositionMillis =
static_cast<double>(m_pTerminalMarker->positionSecs() * 1000);
if (beats.isEmpty()) {
DEBUG_ASSERT(beatPositionMillis == 0);
// If there are no beats yet (because there were no non-terminal
// markers), beatPositionMillis will be 0. In that case we have to find
// the offset backwards from the terminal marker position.
// Because we're working with doubles, we can't use modulo and need to
// implement it ourselves.
const double divisor = std::floor(rangeEndBeatPositionMillis / beatLengthMillis);
beatPositionMillis = rangeEndBeatPositionMillis - (divisor * beatLengthMillis);
} else if (beatPositionMillis > rangeEndBeatPositionMillis) {
beatPositionMillis = rangeEndBeatPositionMillis;
}
// Now fill the range with beats until the end is reached. Add a half beat
// length, to make sure that the last beat is actually included.
while (beatPositionMillis <= (rangeEndBeatPositionMillis + beatLengthMillis / 2)) {
const auto beatPosition = mixxx::audio::FramePos(signalInfo.millis2frames(
beatPositionMillis + timingOffsetMillis));
beats.append(beatPosition.toNearestFrameBoundary());
beatPositionMillis += beatLengthMillis;
}
m_nonTerminalMarkers.clear();
m_pTerminalMarker.reset();
return beats;
}
} // namespace mixxx
|