diff options
Diffstat (limited to 'src/track')
-rw-r--r-- | src/track/cue.cpp | 85 | ||||
-rw-r--r-- | src/track/cue.h | 50 | ||||
-rw-r--r-- | src/track/cueinfo.cpp | 19 | ||||
-rw-r--r-- | src/track/cueinfo.h | 25 | ||||
-rw-r--r-- | src/track/serato/markers.cpp | 10 | ||||
-rw-r--r-- | src/track/serato/markers.h | 1 | ||||
-rw-r--r-- | src/track/serato/markers2.cpp | 42 | ||||
-rw-r--r-- | src/track/serato/markers2.h | 40 | ||||
-rw-r--r-- | src/track/serato/tags.cpp | 81 | ||||
-rw-r--r-- | src/track/serato/tags.h | 68 | ||||
-rw-r--r-- | src/track/track.cpp | 45 | ||||
-rw-r--r-- | src/track/track.h | 8 | ||||
-rw-r--r-- | src/track/trackinfo.cpp | 5 | ||||
-rw-r--r-- | src/track/trackinfo.h | 6 | ||||
-rw-r--r-- | src/track/trackmetadatataglib.cpp | 28 | ||||
-rw-r--r-- | src/track/tracknumbers.cpp | 4 | ||||
-rw-r--r-- | src/track/tracknumbers.h | 2 | ||||
-rw-r--r-- | src/track/trackrecord.cpp | 2 |
18 files changed, 391 insertions, 130 deletions
diff --git a/src/track/cue.cpp b/src/track/cue.cpp index 8831d68596..a923ab7a94 100644 --- a/src/track/cue.cpp +++ b/src/track/cue.cpp @@ -12,7 +12,32 @@ #include "util/color/colorpalette.h" namespace { - const QString kDefaultLabel = ""; // empty string, not null + +inline std::optional<double> positionSamplesToMillis( + double positionSamples, + mixxx::AudioSignal::SampleRate sampleRate) { + VERIFY_OR_DEBUG_ASSERT(sampleRate.valid()) { + return Cue::kNoPosition; + } + if (positionSamples == Cue::kNoPosition) { + return std::nullopt; + } + // Try to avoid rounding errors + return (positionSamples * 1000) / (sampleRate * mixxx::kEngineChannelCount); +} + +inline double positionMillisToSamples( + std::optional<double> positionMillis, + mixxx::AudioSignal::SampleRate sampleRate) { + VERIFY_OR_DEBUG_ASSERT(sampleRate.valid()) { + return Cue::kNoPosition; + } + if (!positionMillis) { + return Cue::kNoPosition; + } + // Try to avoid rounding errors + return (*positionMillis * sampleRate * mixxx::kEngineChannelCount) / 1000; +} } //static @@ -22,20 +47,18 @@ void CuePointer::deleteLater(Cue* pCue) { } } -Cue::Cue(TrackId trackId) +Cue::Cue() : m_bDirty(false), m_iId(-1), - m_trackId(trackId), m_type(mixxx::CueType::Invalid), m_sampleStartPosition(Cue::kNoPosition), m_sampleEndPosition(Cue::kNoPosition), - m_iHotCue(-1), - m_label(kDefaultLabel), + m_iHotCue(Cue::kNoHotCue), m_color(ColorPalette::kDefaultCueColor) { - DEBUG_ASSERT(!m_label.isNull()); } -Cue::Cue(int id, +Cue::Cue( + int id, TrackId trackId, mixxx::CueType type, double position, @@ -51,7 +74,6 @@ Cue::Cue(int id, m_iHotCue(hotCue), m_label(label), m_color(color) { - DEBUG_ASSERT(!m_label.isNull()); if (length) { if (position != Cue::kNoPosition) { m_sampleEndPosition = position + length; @@ -63,34 +85,35 @@ Cue::Cue(int id, } } -Cue::Cue(TrackId trackId, mixxx::AudioSignal::SampleRate sampleRate, const mixxx::CueInfo& cueInfo) +Cue::Cue( + const mixxx::CueInfo& cueInfo, + mixxx::AudioSignal::SampleRate sampleRate) : m_bDirty(false), m_iId(-1), - m_trackId(trackId), m_type(cueInfo.getType()), - m_sampleStartPosition(Cue::kNoPosition), - m_sampleEndPosition(Cue::kNoPosition), - m_iHotCue(Cue::kNoHotCue), + m_sampleStartPosition( + positionMillisToSamples( + cueInfo.getStartPositionMillis(), + sampleRate)), + m_sampleEndPosition( + positionMillisToSamples( + cueInfo.getEndPositionMillis(), + sampleRate)), + m_iHotCue(cueInfo.getHotCueNumber() ? *cueInfo.getHotCueNumber() : kNoHotCue), m_label(cueInfo.getLabel()), m_color(cueInfo.getColor().value_or(ColorPalette::kDefaultCueColor)) { - DEBUG_ASSERT(!m_label.isNull()); - DEBUG_ASSERT(sampleRate.valid()); - - const double sampleRateKhz = sampleRate / 1000.0; - const double millisecsToSamplesFactor = sampleRateKhz * mixxx::kEngineChannelCount; - DEBUG_ASSERT(millisecsToSamplesFactor > 0); - - if (cueInfo.getStartPositionMillis()) { - m_sampleStartPosition = (*cueInfo.getStartPositionMillis()) * millisecsToSamplesFactor; - } - - if (cueInfo.getEndPositionMillis()) { - m_sampleEndPosition = (*cueInfo.getEndPositionMillis()) * millisecsToSamplesFactor; - } +} - if (cueInfo.getHotCueNumber()) { - m_iHotCue = *cueInfo.getHotCueNumber(); - } +mixxx::CueInfo Cue::getCueInfo( + mixxx::AudioSignal::SampleRate sampleRate) const { + QMutexLocker lock(&m_mutex); + return mixxx::CueInfo( + m_type, + positionSamplesToMillis(m_sampleStartPosition, sampleRate), + positionSamplesToMillis(m_sampleEndPosition, sampleRate), + m_iHotCue == kNoHotCue ? std::nullopt : std::make_optional(m_iHotCue), + m_label, + m_color); } int Cue::getId() const { @@ -184,8 +207,6 @@ QString Cue::getLabel() const { } void Cue::setLabel(const QString label) { - //qDebug() << "setLabel()" << m_label << "-" << label; - DEBUG_ASSERT(!label.isNull()); QMutexLocker lock(&m_mutex); m_label = label; m_bDirty = true; diff --git a/src/track/cue.h b/src/track/cue.h index 71e1387e03..14f605c28f 100644 --- a/src/track/cue.h +++ b/src/track/cue.h @@ -1,5 +1,4 @@ -#ifndef MIXXX_CUE_H -#define MIXXX_CUE_H +#pragma once #include <QColor> #include <QMutex> @@ -22,6 +21,10 @@ class Cue : public QObject { static constexpr double kNoPosition = -1.0; static constexpr int kNoHotCue = -1; + Cue(); + Cue( + const mixxx::CueInfo& cueInfo, + mixxx::AudioSignal::SampleRate sampleRate); ~Cue() override = default; bool isDirty() const; @@ -32,32 +35,35 @@ class Cue : public QObject { void setType(mixxx::CueType type); double getPosition() const; - void setStartPosition(double samplePosition); - void setEndPosition(double samplePosition); + void setStartPosition( + double samplePosition = kNoPosition); + void setEndPosition( + double samplePosition = kNoPosition); double getLength() const; int getHotCue() const; - void setHotCue(int hotCue); + void setHotCue( + int hotCue = kNoHotCue); QString getLabel() const; - void setLabel(QString label); + void setLabel( + QString label = QString()); mixxx::RgbColor getColor() const; void setColor(mixxx::RgbColor color); double getEndPosition() const; + mixxx::CueInfo getCueInfo( + mixxx::AudioSignal::SampleRate sampleRate) const; + signals: void updated(); private: - explicit Cue(TrackId trackId); - explicit Cue( - TrackId trackId, - mixxx::AudioSignal::SampleRate sampleRate, - const mixxx::CueInfo& cueInfo); - Cue(int id, + Cue( + int id, TrackId trackId, mixxx::CueType type, double position, @@ -65,7 +71,9 @@ class Cue : public QObject { int hotCue, QString label, mixxx::RgbColor color); + void setDirty(bool dirty); + void setId(int id); void setTrackId(TrackId trackId); @@ -85,11 +93,11 @@ class Cue : public QObject { friend class CueDAO; }; -class CuePointer: public std::shared_ptr<Cue> { +class CuePointer : public std::shared_ptr<Cue> { public: CuePointer() = default; explicit CuePointer(Cue* pCue) - : std::shared_ptr<Cue>(pCue, deleteLater) { + : std::shared_ptr<Cue>(pCue, deleteLater) { } private: @@ -99,9 +107,11 @@ class CuePointer: public std::shared_ptr<Cue> { class CuePosition { public: CuePosition() - : m_position(0.0) {} + : m_position(0.0) { + } CuePosition(double position) - : m_position(position) {} + : m_position(position) { + } double getPosition() const { return m_position; @@ -125,14 +135,10 @@ class CuePosition { bool operator==(const CuePosition& lhs, const CuePosition& rhs); -inline -bool operator!=(const CuePosition& lhs, const CuePosition& rhs) { +inline bool operator!=(const CuePosition& lhs, const CuePosition& rhs) { return !(lhs == rhs); } -inline -QDebug operator<<(QDebug dbg, const CuePosition& arg) { +inline QDebug operator<<(QDebug dbg, const CuePosition& arg) { return dbg << "position =" << arg.getPosition(); } - -#endif // MIXXX_CUE_H diff --git a/src/track/cueinfo.cpp b/src/track/cueinfo.cpp index 66e092a2b6..c22c65f23e 100644 --- a/src/track/cueinfo.cpp +++ b/src/track/cueinfo.cpp @@ -2,10 +2,6 @@ #include "util/assert.h" -namespace { -const QString kDefaultLabel = QStringLiteral(""); // empty string, not null -} // anonymous namespace - namespace mixxx { CueInfo::CueInfo() @@ -13,9 +9,7 @@ CueInfo::CueInfo() m_startPositionMillis(std::nullopt), m_endPositionMillis(std::nullopt), m_hotCueNumber(std::nullopt), - m_label(kDefaultLabel), m_color(std::nullopt) { - DEBUG_ASSERT(!m_label.isNull()); } CueInfo::CueInfo( @@ -31,7 +25,6 @@ CueInfo::CueInfo( m_hotCueNumber(hotCueNumber), m_label(label), m_color(color) { - DEBUG_ASSERT(!m_label.isNull()); } CueType CueInfo::getType() const { @@ -71,7 +64,6 @@ QString CueInfo::getLabel() const { } void CueInfo::setLabel(QString label) { - DEBUG_ASSERT(!label.isNull()); m_label = label; } @@ -83,4 +75,15 @@ void CueInfo::setColor(RgbColor::optional_t color) { m_color = color; } +bool operator==( + const CueInfo& lhs, + const CueInfo& rhs) { + return lhs.getType() == rhs.getType() && + lhs.getStartPositionMillis() == rhs.getStartPositionMillis() && + lhs.getEndPositionMillis() == rhs.getEndPositionMillis() && + lhs.getHotCueNumber() == rhs.getHotCueNumber() && + lhs.getLabel() == rhs.getLabel() && + lhs.getColor() == rhs.getColor(); +} + } // namespace mixxx diff --git a/src/track/cueinfo.h b/src/track/cueinfo.h index 409acd8fd2..5a06688b3d 100644 --- a/src/track/cueinfo.h +++ b/src/track/cueinfo.h @@ -34,20 +34,25 @@ class CueInfo { CueType getType() const; void setType(CueType type); - void setStartPositionMillis(std::optional<double> positionMillis); std::optional<double> getStartPositionMillis() const; + void setStartPositionMillis( + std::optional<double> positionMillis = std::nullopt); - void setEndPositionMillis(std::optional<double> positionMillis); std::optional<double> getEndPositionMillis() const; + void setEndPositionMillis( + std::optional<double> positionMillis = std::nullopt); std::optional<int> getHotCueNumber() const; - void setHotCueNumber(std::optional<int> hotCueNumber); + void setHotCueNumber( + std::optional<int> hotCueNumber = std::nullopt); QString getLabel() const; - void setLabel(QString label); + void setLabel( + QString label = QString()); mixxx::RgbColor::optional_t getColor() const; - void setColor(mixxx::RgbColor::optional_t color); + void setColor( + mixxx::RgbColor::optional_t color = std::nullopt); private: CueType m_type; @@ -58,4 +63,14 @@ class CueInfo { RgbColor::optional_t m_color; }; +bool operator==( + const CueInfo& lhs, + const CueInfo& rhs); + +inline bool operator!=( + const CueInfo& lhs, + const CueInfo& rhs) { + return !(lhs == rhs); +} + } // namespace mixxx diff --git a/src/track/serato/markers.cpp b/src/track/serato/markers.cpp index d3675b7a4c..6c8ee717d0 100644 --- a/src/track/serato/markers.cpp +++ b/src/track/serato/markers.cpp @@ -2,7 +2,7 @@ #include <QtEndian> -#include "util/color/rgbcolor.h" +#include "track/serato/tags.h" namespace { @@ -10,7 +10,6 @@ const int kNumEntries = 14; const int kLoopEntryStartIndex = 5; const int kEntrySize = 22; const quint16 kVersion = 0x0205; -constexpr mixxx::RgbColor kDefaultTrackColor = mixxx::RgbColor(0xFF9999); // These functions conversion between the 4-byte "Serato Markers_" color format // and RgbColor (3-Byte RGB, transparency disabled). @@ -252,6 +251,11 @@ bool SeratoMarkers::parse(SeratoMarkers* seratoMarkers, const QByteArray& data) QByteArray SeratoMarkers::dump() const { QByteArray data; + if (isEmpty()) { + // Return empty QByteArray + return data; + } + data.resize(sizeof(quint16) + 2 * sizeof(quint32) + kEntrySize * m_entries.size()); QDataStream stream(&data, QIODevice::WriteOnly); @@ -262,7 +266,7 @@ QByteArray SeratoMarkers::dump() const { SeratoMarkersEntryPointer pEntry = m_entries.at(i); stream.writeRawData(pEntry->dump(), kEntrySize); } - stream << seratoColorFromRgb(m_trackColor.value_or(kDefaultTrackColor)); + stream << seratoColorFromRgb(m_trackColor.value_or(SeratoTags::kDefaultTrackColor)); return data; } diff --git a/src/track/serato/markers.h b/src/track/serato/markers.h index b81f7b5563..48e6700978 100644 --- a/src/track/serato/markers.h +++ b/src/track/serato/markers.h @@ -11,7 +11,6 @@ namespace mixxx { -// Forward declaration class SeratoMarkersEntry; typedef std::shared_ptr<SeratoMarkersEntry> SeratoMarkersEntryPointer; diff --git a/src/track/serato/markers2.cpp b/src/track/serato/markers2.cpp index e9e93a75ff..fb6728b0b5 100644 --- a/src/track/serato/markers2.cpp +++ b/src/track/serato/markers2.cpp @@ -372,6 +372,18 @@ bool SeratoMarkers2::parse(SeratoMarkers2* seratoMarkers2, const QByteArray& out QByteArray SeratoMarkers2::dump() const { QByteArray data; + + // To reduce disk fragmentation, Serato pre-allocates at least 470 bytes + // for the "Markers2" tag. Unused bytes are filled with null-bytes. + // Hence, it's possible to have a valid tag that does not contain actual + // marker information. The allocated size is set after successfully parsing + // the tag, so if the tag is valid but does not contain entries we + // shouldn't delete the tag content. + if (isEmpty() && getAllocatedSize() == 0) { + // Return empty QByteArray + return data; + } + QDataStream stream(&data, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_5_0); stream.setByteOrder(QDataStream::BigEndian); @@ -426,4 +438,34 @@ QByteArray SeratoMarkers2::dump() const { return outerData.leftJustified(size, '\0'); } +RgbColor::optional_t SeratoMarkers2::getTrackColor() const { + qDebug() << "Reading track color from 'Serato Markers2' tag data..."; + + for (auto& pEntry : m_entries) { + DEBUG_ASSERT(pEntry); + if (pEntry->typeId() != SeratoMarkers2Entry::TypeId::Color) { + continue; + } + const SeratoMarkers2ColorEntry* pColorEntry = static_cast<SeratoMarkers2ColorEntry*>(pEntry.get()); + return RgbColor::optional(pColorEntry->getColor()); + } + + return std::nullopt; +} + +bool SeratoMarkers2::isBpmLocked() const { + qDebug() << "Reading bpmlock state from 'Serato Markers2' tag data..."; + + for (auto& pEntry : m_entries) { + DEBUG_ASSERT(pEntry); + if (pEntry->typeId() != SeratoMarkers2Entry::TypeId::Bpmlock) { + continue; + } + const SeratoMarkers2BpmlockEntry* pBpmlockEntry = static_cast<SeratoMarkers2BpmlockEntry*>(pEntry.get()); + return pBpmlockEntry->isLocked(); + } + + return false; +} + } //namespace mixxx diff --git a/src/track/serato/markers2.h b/src/track/serato/markers2.h index 3b8fa99e00..e6800a39b2 100644 --- a/src/track/serato/markers2.h +++ b/src/track/serato/markers2.h @@ -9,11 +9,6 @@ #include "util/color/rgbcolor.h" #include "util/types.h" -namespace { -constexpr mixxx::RgbColor kDefaultTrackColor = mixxx::RgbColor(0xFF9999); -constexpr mixxx::RgbColor kDefaultCueColor = mixxx::RgbColor(0xCC0000); -} // namespace - namespace mixxx { // Enum values need to appear in the same order as the corresponding entries @@ -61,6 +56,7 @@ class SeratoMarkers2UnknownEntry : public SeratoMarkers2Entry { : m_type(std::move(type)), m_data(std::move(data)) { } + SeratoMarkers2UnknownEntry() = delete; ~SeratoMarkers2UnknownEntry() override = default; QString type() const override { @@ -89,10 +85,7 @@ class SeratoMarkers2BpmlockEntry : public SeratoMarkers2Entry { SeratoMarkers2BpmlockEntry(bool locked) : m_locked(locked) { } - - SeratoMarkers2BpmlockEntry() - : m_locked(false) { - } + SeratoMarkers2BpmlockEntry() = delete; static SeratoMarkers2EntryPointer parse(const QByteArray& data); @@ -139,10 +132,7 @@ class SeratoMarkers2ColorEntry : public SeratoMarkers2Entry { SeratoMarkers2ColorEntry(RgbColor color) : m_color(color) { } - - SeratoMarkers2ColorEntry() - : m_color(kDefaultTrackColor) { - } + SeratoMarkers2ColorEntry() = delete; static SeratoMarkers2EntryPointer parse(const QByteArray& data); @@ -192,13 +182,7 @@ class SeratoMarkers2CueEntry : public SeratoMarkers2Entry { m_color(color), m_label(label) { } - - SeratoMarkers2CueEntry() - : m_index(0), - m_position(0), - m_color(kDefaultCueColor), - m_label(QString("")) { - } + SeratoMarkers2CueEntry() = delete; static SeratoMarkers2EntryPointer parse(const QByteArray& data); @@ -282,14 +266,7 @@ class SeratoMarkers2LoopEntry : public SeratoMarkers2Entry { m_locked(locked), m_label(label) { } - - SeratoMarkers2LoopEntry() - : m_index(0), - m_startposition(0), - m_endposition(0), - m_locked(false), - m_label(QString("")) { - } + SeratoMarkers2LoopEntry() = delete; static SeratoMarkers2EntryPointer parse(const QByteArray& data); @@ -386,7 +363,9 @@ inline QDebug operator<<(QDebug dbg, const SeratoMarkers2LoopEntry& arg) { // class SeratoMarkers2 final { public: - SeratoMarkers2() = default; + SeratoMarkers2() + : m_allocatedSize(0) { + } explicit SeratoMarkers2( QList<std::shared_ptr<SeratoMarkers2Entry>> entries) : m_allocatedSize(0), @@ -419,6 +398,9 @@ class SeratoMarkers2 final { m_entries = std::move(entries); } + RgbColor::optional_t getTrackColor() const; + bool isBpmLocked() const; + private: int m_allocatedSize; QList<std::shared_ptr<SeratoMarkers2Entry>> m_entries; diff --git a/src/track/serato/tags.cpp b/src/track/serato/tags.cpp new file mode 100644 index 0000000000..b621a5d81e --- /dev/null +++ b/src/track/serato/tags.cpp @@ -0,0 +1,81 @@ +#include "track/serato/tags.h" + +namespace mixxx { + +RgbColor::optional_t SeratoTags::storedToDisplayedTrackColor(RgbColor color) { + // Serato stores Track colors differently from how they are displayed in + // the library column. Instead of the color from the library view, the + // value from the color picker is stored instead (which is different). + // To make sure that the track looks the same in both Mixxx' and Serato's + // libraries, we need to convert between the two values. + // + // See this for details: + // https://github.com/Holzhaus/serato-tags/blob/master/docs/colors.md#track-colors + + if (color == 0xFFFFFF) { + return RgbColor::nullopt(); + } + + if (color == 0x999999) { + return RgbColor::optional(0x090909); + } + + if (color == 0x000000) { + return RgbColor::optional(0x333333); + } + + RgbColor::code_t colorCode = color; + colorCode = (colorCode < 0x666666) ? colorCode + 0x99999A : colorCode - 0x666666; + return RgbColor::optional(colorCode); +} + +RgbColor SeratoTags::displayedToStoredTrackColor(RgbColor::optional_t color) { + if (!color) { + return RgbColor(0xFFFFFF); + } + + RgbColor::code_t colorCode = *color; + + if (colorCode == 0x090909) { + return RgbColor(0x999999); + } + + if (colorCode == 0x333333) { + return RgbColor(0x000000); + } + + // Special case: 0x999999 and 0x99999a are not representable as Serato + // track color We'll just modify them a little, so that the look the + // same in Serato. + if (colorCode == 0x999999) { + return RgbColor(0x999998); + } + + if (colorCode == 0x99999a) { + return RgbColor(0x99999b); + } + + colorCode = (colorCode < 0x99999A) ? colorCode + 0x666666 : colorCode - 0x99999A; + return RgbColor(colorCode); +} + +RgbColor::optional_t SeratoTags::getTrackColor() const { + RgbColor::optional_t color = m_seratoMarkers.getTrackColor(); + + if (!color) { + // Markers_ is empty, but we may have a color in Markers2 + color = m_seratoMarkers2.getTrackColor(); + } + + if (color) { + color = SeratoTags::storedToDisplayedTrackColor(*color); + } + + return color; +} + +bool SeratoTags::isBpmLocked() const { + return m_seratoMarkers2.isBpmLocked(); +} + +} // namespace mixxx diff --git a/src/track/serato/tags.h b/src/track/serato/tags.h new file mode 100644 index 0000000000..d2bd460cc0 --- /dev/null +++ b/src/track/serato/tags.h @@ -0,0 +1,68 @@ +#pragma once + +#include "track/serato/markers.h" +#include "track/serato/markers2.h" + +namespace mixxx { + +// DTO for storing information from the SeratoMarkers_/2 tags used by the +// Serato DJ Pro software. +// +class SeratoTags final { + public: + static constexpr RgbColor kDefaultTrackColor = RgbColor(0xFF9999); + static constexpr RgbColor kDefaultCueColor = RgbColor(0xCC0000); + + SeratoTags() = default; + + static RgbColor::optional_t storedToDisplayedTrackColor(RgbColor color); + static RgbColor displayedToStoredTrackColor(RgbColor::optional_t color); + + bool isEmpty() const { + return m_seratoMarkers.isEmpty() && m_seratoMarkers2.isEmpty(); + } + + bool parseMarkers(const QByteArray& data) { + return SeratoMarkers::parse(&m_seratoMarkers, data); + } + + bool parseMarkers2(const QByteArray& data) { + return SeratoMarkers2::parse(&m_seratoMarkers2, data); + } + + QByteArray dumpMarkers() const { + return m_seratoMarkers.dump(); + } + + QByteArray dumpMarkers2() const { + return m_seratoMarkers2.dump(); + } + + RgbColor::optional_t getTrackColor() const; + bool isBpmLocked() const; + + private: + SeratoMarkers m_seratoMarkers; + SeratoMarkers2 m_seratoMarkers2; +}; + +inline bool operator==(const SeratoTags& lhs, const SeratoTags& rhs) { + // FIXME: Find a more efficient way to do this + return (lhs.dumpMarkers() == rhs.dumpMarkers() && lhs.dumpMarkers2() == rhs.dumpMarkers2()); +} + +inline bool operator!=(const SeratoTags& lhs, const SeratoTags& rhs) { + return !(lhs == rhs); +} + +inline QDebug operator<<(QDebug dbg, const SeratoTags& arg) { + Q_UNUSED(arg); + + // TODO + return dbg << "SeratoTags"; +} + +} // namespace mixxx + +Q_DECLARE_TYPEINFO(mixxx::SeratoTags, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(mixxx::SeratoTags) diff --git a/src/track/track.cpp b/src/track/track.cpp index ef9fc9d593..27e04c8282 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -128,7 +128,9 @@ void Track::importMetadata( const auto newBpm = importedMetadata.getTrackInfo().getBpm(); const auto newKey = importedMetadata.getTrackInfo().getKey(); const auto newReplayGain = importedMetadata.getTrackInfo().getReplayGain(); - +#ifdef __EXTRA_METADATA__ + const auto newSeratoTags = importedMetadata.getTrackInfo().getSeratoTags(); +#endif // __EXTRA_METADATA__ { // enter locking scope QMutexLocker lock(&m_qMutex); @@ -151,6 +153,12 @@ void Track::importMetadata( emit ReplayGainUpdated(newReplayGain); } } + +#ifdef __EXTRA_METADATA__ + setColor(newSeratoTags.getTrackColor()); + setBpmLocked(newSeratoTags.isBpmLocked()); +#endif // __EXTRA_METADATA__ + // implicitly unlocked when leaving scope } @@ -675,7 +683,8 @@ void Track::setCuePoint(CuePosition cue) { double position = cue.getPosition(); if (position != -1.0) { if (!pLoadCue) { - pLoadCue = CuePointer(new Cue(m_record.getId())); + pLoadCue = CuePointer(new Cue()); + pLoadCue->setTrackId(m_record.getId()); pLoadCue->setType(mixxx::CueType::MainCue); connect(pLoadCue.get(), &Cue::updated, @@ -705,7 +714,8 @@ void Track::slotCueUpdated() { CuePointer Track::createAndAddCue() { QMutexLocker lock(&m_qMutex); - CuePointer pCue(new Cue(m_record.getId())); + CuePointer pCue(new Cue()); + pCue->setTrackId(m_record.getId()); connect(pCue.get(), &Cue::updated, this, &Track::slotCueUpdated); m_cuePoints.push_back(pCue); markDirtyAndUnlock(&lock); @@ -781,6 +791,8 @@ void Track::setCuePoints(const QList<CuePointer>& cuePoints) { // connect new cue points for (const auto& pCue: m_cuePoints) { connect(pCue.get(), &Cue::updated, this, &Track::slotCueUpdated); + // Enure that the track IDs are correct + pCue->setTrackId(m_record.getId()); // update main cue point if (pCue->getType() == mixxx::CueType::MainCue) { m_record.setCuePoint(CuePosition(pCue->getPosition())); @@ -801,7 +813,8 @@ void Track::importCuePoints(const QList<mixxx::CueInfo>& cueInfos) { QList<CuePointer> cuePoints; for (const mixxx::CueInfo& cueInfo : cueInfos) { - CuePointer pCue(new Cue(trackId, sampleRate, cueInfo)); + CuePointer pCue(new Cue(cueInfo, sampleRate)); + pCue->setTrackId(trackId); cuePoints.append(pCue); } setCuePoints(cuePoints); @@ -949,6 +962,30 @@ void Track::setCoverInfo(const CoverInfoRelative& coverInfo) { } } +bool Track::refreshCoverImageHash( + const QImage& loadedImage) { + QMutexLocker lock(&m_qMutex); + auto coverInfo = CoverInfo( + m_record.getCoverInfo(), + m_fileInfo.location()); + if (!coverInfo.refreshImageHash( + loadedImage, + m_pSecurityToken)) { + return false; + } + if (!compareAndSet( + &m_record.refCoverInfo(), + static_cast<const CoverInfoRelative&>(coverInfo))) { + return false; + } + kLogger.info() + << "Refreshed cover image hash" + << m_fileInfo.location(); + markDirtyAndUnlock(&lock); + emit coverArtUpdated(); + return true; +} + CoverInfoRelative Track::getCoverInfo() const { QMutexLocker lock(&m_qMutex); return m_record.getCoverInfo(); diff --git a/src/track/track.h b/src/track/track.h index 266da4d00d..78965cf65d 100644 --- a/src/track/track.h +++ b/src/track/track.h @@ -279,13 +279,19 @@ class Track : public QObject { void setCoverInfo(const CoverInfoRelative& coverInfo); CoverInfoRelative getCoverInfo() const; CoverInfo getCoverInfoWithLocation() const; + // Verify the cover image hash and update it if necessary. + // If the corresponding image has already been loaded it + // could be provided as a parameter to avoid reloading + // if actually needed. + bool refreshCoverImageHash( + const QImage& loadedImage = QImage()); quint16 getCoverHash() const; // Set/get track metadata and cover art (optional) all at once. void importMetadata( mixxx::TrackMetadata importedMetadata, - QDateTime metadataSynchronized); + QDateTime metadataSynchronized = QDateTime()); // Merge additional metadata that is not (yet) stored in the database // and only available from file tags. void mergeImportedMetadata( diff --git a/src/track/trackinfo.cpp b/src/track/trackinfo.cpp index 72ed440a81..4adcdee652 100644 --- a/src/track/trackinfo.cpp +++ b/src/track/trackinfo.cpp @@ -75,8 +75,7 @@ bool TrackInfo::compareEq( #endif // __EXTRA_METADATA__ (getReplayGain() == trackInfo.getReplayGain()) && #if defined(__EXTRA_METADATA__) - (getSeratoMarkers() == trackInfo.getSeratoMarkers()) && - (getSeratoMarkers2() == trackInfo.getSeratoMarkers2()) && + (getSeratoTags() == trackInfo.getSeratoTags()) && (getSubtitle() == trackInfo.getSubtitle()) && #endif // __EXTRA_METADATA__ (getTitle() == trackInfo.getTitle()) && @@ -120,7 +119,7 @@ QDebug operator<<(QDebug dbg, const TrackInfo& arg) { #endif // __EXTRA_METADATA__ arg.dbgReplayGain(dbg); #if defined(__EXTRA_METADATA__) - arg.dbgSeratoMarkers2(dbg); + arg.dbgSeratoTags(dbg); arg.dbgSubtitle(dbg); #endif // __EXTRA_METADATA__ arg.dbgTitle(dbg); diff --git a/src/track/trackinfo.h b/src/track/trackinfo.h index 478e87001c..367a4eb53d 100644 --- a/src/track/trackinfo.h +++ b/src/track/trackinfo.h @@ -6,8 +6,7 @@ #include "sources/audiosource.h" #include "track/bpm.h" #include "track/replaygain.h" -#include "track/serato/markers.h" -#include "track/serato/markers2.h" +#include "track/serato/tags.h" #include "util/duration.h" #include "util/macros.h" @@ -45,8 +44,7 @@ class TrackInfo final { #endif // __EXTRA_METADATA__ PROPERTY_SET_BYVAL_GET_BYREF(ReplayGain, replayGain, ReplayGain) #if defined(__EXTRA_METADATA__) - PROPERTY_SET_BYVAL_GET_BYREF(SeratoMarkers, seratoMarkers, SeratoMarkers) - PROPERTY_SET_BYVAL_GET_BYREF(SeratoMarkers2, seratoMarkers2, SeratoMarkers2) + PROPERTY_SET_BYVAL_GET_BYREF(Ser |