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