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
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#include <QtDebug>
#include <QStringList>
#include "track/beatgrid.h"
#include "track/beatmap.h"
#include "track/beatfactory.h"
#include "track/beatutils.h"
BeatsPointer BeatFactory::loadBeatsFromByteArray(const Track& track,
QString beatsVersion,
QString beatsSubVersion,
const QByteArray& beatsSerialized) {
if (beatsVersion == BEAT_GRID_1_VERSION ||
beatsVersion == BEAT_GRID_2_VERSION) {
BeatGrid* pGrid = new BeatGrid(track, 0, beatsSerialized);
pGrid->setSubVersion(beatsSubVersion);
qDebug() << "Successfully deserialized BeatGrid";
return BeatsPointer(pGrid, &BeatFactory::deleteBeats);
} else if (beatsVersion == BEAT_MAP_VERSION) {
BeatMap* pMap = new BeatMap(track, 0, beatsSerialized);
pMap->setSubVersion(beatsSubVersion);
qDebug() << "Successfully deserialized BeatMap";
return BeatsPointer(pMap, &BeatFactory::deleteBeats);
}
qDebug() << "BeatFactory::loadBeatsFromByteArray could not parse serialized beats.";
return BeatsPointer();
}
BeatsPointer BeatFactory::makeBeatGrid(const Track& track, double dBpm,
double dFirstBeatSample) {
BeatGrid* pGrid = new BeatGrid(track, 0);
pGrid->setGrid(dBpm, dFirstBeatSample);
return BeatsPointer(pGrid, &BeatFactory::deleteBeats);
}
// static
QString BeatFactory::getPreferredVersion(const bool bEnableFixedTempoCorrection) {
if (bEnableFixedTempoCorrection) {
return BEAT_GRID_2_VERSION;
}
return BEAT_MAP_VERSION;
}
QString BeatFactory::getPreferredSubVersion(
const bool bEnableFixedTempoCorrection,
const bool bEnableOffsetCorrection,
const int iMinBpm, const int iMaxBpm,
const QHash<QString, QString> extraVersionInfo) {
const char* kSubVersionKeyValueSeparator = "=";
const char* kSubVersionFragmentSeparator = "|";
QStringList fragments;
// min/max BPM limits only apply to fixed-tempo assumption
if (bEnableFixedTempoCorrection) {
fragments << QString("min_bpm%1%2").arg(kSubVersionKeyValueSeparator,
QString::number(iMinBpm));
fragments << QString("max_bpm%1%2").arg(kSubVersionKeyValueSeparator,
QString::number(iMaxBpm));
}
QHashIterator<QString, QString> it(extraVersionInfo);
while (it.hasNext()) {
it.next();
if (it.key().contains(kSubVersionKeyValueSeparator) ||
it.key().contains(kSubVersionFragmentSeparator) ||
it.value().contains(kSubVersionKeyValueSeparator) ||
it.value().contains(kSubVersionFragmentSeparator)) {
qDebug() << "ERROR: Your analyzer key/value contains invalid characters:"
<< it.key() << ":" << it.value() << "Skipping.";
continue;
}
fragments << QString("%1%2%3").arg(
it.key(), kSubVersionKeyValueSeparator, it.value());
}
if (bEnableFixedTempoCorrection && bEnableOffsetCorrection) {
fragments << QString("offset_correction%1%2")
.arg(kSubVersionKeyValueSeparator, QString::number(1));
}
fragments << QString("rounding%1%2").
arg(kSubVersionKeyValueSeparator, QString::number(0.05));
std::sort(fragments.begin(), fragments.end());
return (fragments.size() > 0) ? fragments.join(kSubVersionFragmentSeparator) : "";
}
BeatsPointer BeatFactory::makePreferredBeats(
const Track& track, QVector<double> beats,
const QHash<QString, QString> extraVersionInfo,
const bool bEnableFixedTempoCorrection, const bool bEnableOffsetCorrection,
const int iSampleRate, const int iTotalSamples,
const int iMinBpm, const int iMaxBpm) {
const QString version = getPreferredVersion(bEnableFixedTempoCorrection);
const QString subVersion = getPreferredSubVersion(bEnableFixedTempoCorrection,
bEnableOffsetCorrection,
iMinBpm, iMaxBpm,
extraVersionInfo);
BeatUtils::printBeatStatistics(beats, iSampleRate);
if (version == BEAT_GRID_2_VERSION) {
double globalBpm = BeatUtils::calculateBpm(beats, iSampleRate, iMinBpm, iMaxBpm);
double firstBeat = BeatUtils::calculateFixedTempoFirstBeat(
bEnableOffsetCorrection,
beats, iSampleRate, iTotalSamples, globalBpm);
BeatGrid* pGrid = new BeatGrid(track, iSampleRate);
// firstBeat is in frames here and setGrid() takes samples.
pGrid->setGrid(globalBpm, firstBeat * 2);
pGrid->setSubVersion(subVersion);
return BeatsPointer(pGrid, &BeatFactory::deleteBeats);
} else if (version == BEAT_MAP_VERSION) {
BeatMap* pBeatMap = new BeatMap(track, iSampleRate, beats);
pBeatMap->setSubVersion(subVersion);
return BeatsPointer(pBeatMap, &BeatFactory::deleteBeats);
} else {
qDebug() << "ERROR: Could not determine what type of beatgrid to create.";
return BeatsPointer();
}
}
void BeatFactory::deleteBeats(Beats* pBeats) {
// BeatGrid/BeatMap objects have no parent and live in the same thread as
// their associated TIO. QObject::deleteLater does not have the desired
// effect when the QObject's thread does not have an event loop (i.e. when
// the main thread has already shut down) so we delete the BeatMap/BeatGrid
// directly when its reference count drops to zero.
delete pBeats;
}
|