From 41f37179a2fcc621397518ee8c28d29484f88580 Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Sat, 11 Apr 2020 02:22:30 +0200 Subject: [PATCH] track/serato: Import saved loops from serato's tags --- src/track/serato/markers.cpp | 17 ++++++++++++- src/track/serato/markers2.cpp | 18 ++++++++++++-- src/track/serato/tags.cpp | 45 ++++++++++++++++++++++++++++++----- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/track/serato/markers.cpp b/src/track/serato/markers.cpp index 4299cdcf06a..a3bf082265d 100644 --- a/src/track/serato/markers.cpp +++ b/src/track/serato/markers.cpp @@ -289,6 +289,7 @@ QList SeratoMarkers::getCues(double timingOffsetMillis) const { QList cueInfos; int cueIndex = 0; + int loopIndex = 0; for (const auto& pEntry : m_entries) { DEBUG_ASSERT(pEntry); switch (pEntry->typeId()) { @@ -306,7 +307,21 @@ QList SeratoMarkers::getCues(double timingOffsetMillis) const { cueIndex++; break; } - // TODO: Add support for Loops + case SeratoMarkersEntry::TypeId::Loop: { + if (pEntry->hasStartPosition()) { + CueInfo loopInfo = CueInfo( + CueType::Loop, + pEntry->getStartPosition() + timingOffsetMillis, + pEntry->getEndPosition() + timingOffsetMillis, + loopIndex, + "", + std::nullopt); + cueInfos.append(loopInfo); + // TODO: Add support for the "locked" attribute + } + loopIndex++; + break; + } default: break; } diff --git a/src/track/serato/markers2.cpp b/src/track/serato/markers2.cpp index b25ad5c287d..dd59fcb1c50 100644 --- a/src/track/serato/markers2.cpp +++ b/src/track/serato/markers2.cpp @@ -440,6 +440,7 @@ QByteArray SeratoMarkers2::dump() const { QList SeratoMarkers2::getCues(double timingOffsetMillis) const { qDebug() << "Reading cues from 'Serato Markers2' tag data..."; + QList cueInfos; for (auto& pEntry : m_entries) { DEBUG_ASSERT(pEntry); @@ -454,10 +455,23 @@ QList SeratoMarkers2::getCues(double timingOffsetMillis) const { pCueEntry->getLabel(), pCueEntry->getColor()); cueInfos.append(cueInfo); - break; } - // TODO: Add support for LOOP/FLIP + case SeratoMarkers2Entry::TypeId::Loop: { + const SeratoMarkers2LoopEntry* pLoopEntry = + static_cast(pEntry.get()); + CueInfo loopInfo = CueInfo( + CueType::Loop, + pLoopEntry->getStartPosition() + timingOffsetMillis, + pLoopEntry->getEndPosition() + timingOffsetMillis, + pLoopEntry->getIndex(), + pLoopEntry->getLabel(), + std::nullopt); // Serato's Loops don't have a color + // TODO: Add support for "locked" loops + cueInfos.append(loopInfo); + break; + } + // TODO: Add support for FLIP default: break; } diff --git a/src/track/serato/tags.cpp b/src/track/serato/tags.cpp index e37c35bd686..8065682957b 100644 --- a/src/track/serato/tags.cpp +++ b/src/track/serato/tags.cpp @@ -16,6 +16,8 @@ const QString kDecoderName(QStringLiteral("FFMPEG")); const QString kDecoderName(QStringLiteral("Unknown")); #endif +const int kLoopIndexOffset = 8; + mixxx::RgbColor getColorFromOtherPalette( const ColorPalette& source, const ColorPalette& dest, @@ -156,6 +158,11 @@ QList SeratoTags::getCues(const QString& filePath) const { // from "Serato Markers_". This is what Serato does too (i.e. if // "Serato Markers_" and "Serato Markers2" contradict each other, // Serato will use the values from "Serato Markers_"). + // + // In Serato, loops and hotcues are kept separate (i. e. you can + // have a loop and a hotcue with the same number. In Mixxx, loops + // and hotcues share indices. Hence, we import them with and offset + // of 8 (the maximum number of hotcues in Serato). double timingOffsetMillis = SeratoTags::findTimingOffsetMillis(filePath); @@ -171,9 +178,22 @@ QList SeratoTags::getCues(const QString& filePath) const { qWarning() << "SeratoTags::getCues: Cue with number < 0 found!"; } - if (cueInfo.getType() != CueType::HotCue) { - qWarning() << "SeratoTags::getCues: Ignoring cue with non-hotcue type!"; - continue; + switch (cueInfo.getType()) { + case CueType::HotCue: { + if (index >= kLoopIndexOffset) { + qWarning() + << "SeratoTags::getCues: Non-loop Cue with number >=" + << kLoopIndexOffset << "found!"; + continue; + } + break; + } + case CueType::Loop: { + index += kLoopIndexOffset; + break; + } + default: + break; } CueInfo newCueInfo(cueInfo); @@ -200,9 +220,22 @@ QList SeratoTags::getCues(const QString& filePath) const { qWarning() << "SeratoTags::getCues: Cue with number < 0 found!"; } - if (cueInfo.getType() != CueType::HotCue) { - qWarning() << "SeratoTags::getCues: Ignoring cue with non-hotcue type!"; - continue; + switch (cueInfo.getType()) { + case CueType::HotCue: { + if (index >= kLoopIndexOffset) { + qWarning() + << "SeratoTags::getCues: Non-loop Cue with number >=" + << kLoopIndexOffset << "found!"; + continue; + } + break; + } + case CueType::Loop: { + index += kLoopIndexOffset; + break; + } + default: + break; } // Take a pre-existing CueInfo object that was read from