1 #include <QtDebug>
2 #include <QStringList>
3
4 #include "track/beatgrid.h"
5 #include "track/beatmap.h"
6 #include "track/beatfactory.h"
7 #include "track/beatutils.h"
8
9 namespace {
10
11 const QString kRoundingVersion = QStringLiteral("V4");
12
13 } // namespace
14
loadBeatsFromByteArray(mixxx::audio::SampleRate sampleRate,const QString & beatsVersion,const QString & beatsSubVersion,const QByteArray & beatsSerialized)15 mixxx::BeatsPointer BeatFactory::loadBeatsFromByteArray(
16 mixxx::audio::SampleRate sampleRate,
17 const QString& beatsVersion,
18 const QString& beatsSubVersion,
19 const QByteArray& beatsSerialized) {
20 if (beatsVersion == BEAT_GRID_1_VERSION ||
21 beatsVersion == BEAT_GRID_2_VERSION) {
22 auto pGrid = mixxx::BeatGrid::makeBeatGrid(sampleRate, beatsSubVersion, beatsSerialized);
23 qDebug() << "Successfully deserialized BeatGrid";
24 return pGrid;
25 } else if (beatsVersion == BEAT_MAP_VERSION) {
26 auto pMap = mixxx::BeatMap::makeBeatMap(sampleRate, beatsSubVersion, beatsSerialized);
27 qDebug() << "Successfully deserialized BeatMap";
28 return pMap;
29 }
30 qDebug() << "BeatFactory::loadBeatsFromByteArray could not parse serialized beats.";
31 return mixxx::BeatsPointer();
32 }
33
makeBeatGrid(mixxx::audio::SampleRate sampleRate,double dBpm,double dFirstBeatSample)34 mixxx::BeatsPointer BeatFactory::makeBeatGrid(
35 mixxx::audio::SampleRate sampleRate,
36 double dBpm,
37 double dFirstBeatSample) {
38 return mixxx::BeatGrid::makeBeatGrid(sampleRate, QString(), dBpm, dFirstBeatSample);
39 }
40
41 // static
getPreferredVersion(bool fixedTempo)42 QString BeatFactory::getPreferredVersion(bool fixedTempo) {
43 if (fixedTempo) {
44 return BEAT_GRID_2_VERSION;
45 }
46 return BEAT_MAP_VERSION;
47 }
48
getPreferredSubVersion(const QHash<QString,QString> & extraVersionInfo)49 QString BeatFactory::getPreferredSubVersion(
50 const QHash<QString, QString>& extraVersionInfo) {
51 const char* kSubVersionKeyValueSeparator = "=";
52 const char* kSubVersionFragmentSeparator = "|";
53 QStringList fragments;
54
55 QHashIterator<QString, QString> it(extraVersionInfo);
56 while (it.hasNext()) {
57 it.next();
58 if (it.key().contains(kSubVersionKeyValueSeparator) ||
59 it.key().contains(kSubVersionFragmentSeparator) ||
60 it.value().contains(kSubVersionKeyValueSeparator) ||
61 it.value().contains(kSubVersionFragmentSeparator)) {
62 qDebug() << "ERROR: Your analyzer key/value contains invalid "
63 "characters:"
64 << it.key() << ":" << it.value() << "Skipping.";
65 continue;
66 }
67 fragments << QString("%1%2%3").arg(
68 it.key(), kSubVersionKeyValueSeparator, it.value());
69 }
70
71 fragments << QString("rounding%1%2")
72 .arg(kSubVersionKeyValueSeparator, kRoundingVersion);
73
74 std::sort(fragments.begin(), fragments.end());
75 return (fragments.size() > 0) ? fragments.join(kSubVersionFragmentSeparator)
76 : "";
77 }
78
makePreferredBeats(const QVector<double> & beats,const QHash<QString,QString> & extraVersionInfo,bool fixedTempo,mixxx::audio::SampleRate sampleRate)79 mixxx::BeatsPointer BeatFactory::makePreferredBeats(
80 const QVector<double>& beats,
81 const QHash<QString, QString>& extraVersionInfo,
82 bool fixedTempo,
83 mixxx::audio::SampleRate sampleRate) {
84 const QString version = getPreferredVersion(fixedTempo);
85 const QString subVersion = getPreferredSubVersion(extraVersionInfo);
86
87 #ifdef DEBUG_PRINT_BEATS
88 for (double beat : beats) {
89 qDebug().noquote() << QString::number(beat, 'g', 8);
90 }
91 #endif
92
93 QVector<BeatUtils::ConstRegion> constantRegions =
94 BeatUtils::retrieveConstRegions(beats, sampleRate);
95
96 #ifdef DEBUG_PRINT_BEATS
97 for (auto& region : constantRegions) {
98 qDebug().noquote() << QString::number(region.firstBeat, 'g', 8)
99 << QString::number(region.beatLength, 'g', 8);
100 }
101 #endif
102
103 if (version == BEAT_GRID_2_VERSION) {
104 double firstBeat = 0;
105 double constBPM = BeatUtils::makeConstBpm(constantRegions, sampleRate, &firstBeat);
106 firstBeat = BeatUtils::adjustPhase(firstBeat, constBPM, sampleRate, beats);
107 auto pGrid = mixxx::BeatGrid::makeBeatGrid(
108 sampleRate, subVersion, constBPM, firstBeat * 2);
109 return pGrid;
110 } else if (version == BEAT_MAP_VERSION) {
111 QVector<double> ironedBeats = BeatUtils::getBeats(constantRegions);
112 auto pBeatMap = mixxx::BeatMap::makeBeatMap(sampleRate, subVersion, ironedBeats);
113 return pBeatMap;
114 } else {
115 qDebug() << "ERROR: Could not determine what type of beatgrid to create.";
116 return mixxx::BeatsPointer();
117 }
118 }
119