Skip to content

Commit

Permalink
add controls to sort hotcues by position, optionally remove empty slo…
Browse files Browse the repository at this point in the history
…ts/offsets

`sort_hotcues`: by pos.: 3, 7, 2 -> 2, 3, 7
`sort_hotcues_compress`: 3, 7, 2 -> 1, 2, 3
  • Loading branch information
ronso0 committed Jan 31, 2025
1 parent 9849bab commit 2b82059
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/audio/frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <limits>

#include "engine/engine.h"
#include "util/compatibility/qhash.h"
#include "util/fpclassify.h"

namespace mixxx {
Expand Down Expand Up @@ -247,6 +248,12 @@ inline bool operator!=(FramePos frame1, FramePos frame2) {

QDebug operator<<(QDebug dbg, FramePos arg);

inline qhash_seed_t qHash(
FramePos pos,
qhash_seed_t seed = 0) {
return static_cast<qhash_seed_t>(pos.value(), seed);
}

constexpr FramePos kInvalidFramePos = FramePos(FramePos::kInvalidValue);
constexpr FramePos kStartFramePos = FramePos(FramePos::kStartValue);
} // namespace audio
Expand Down
26 changes: 26 additions & 0 deletions src/engine/controls/cuecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ CueControl::CueControl(const QString& group,
m_pPassthrough->connectValueChanged(this,
&CueControl::passthroughChanged,
Qt::DirectConnection);

m_pSortHotcuesByPos = std::make_unique<ControlPushButton>(ConfigKey(group, "sort_hotcues"));
connect(m_pSortHotcuesByPos.get(),
&ControlObject::valueChanged,
this,
&CueControl::setHotcueIndicesSortedByPosition,
Qt::DirectConnection);
m_pSortHotcuesByPosCompress = std::make_unique<ControlPushButton>(
ConfigKey(group, "sort_hotcues_remove_offsets"));
connect(m_pSortHotcuesByPosCompress.get(),
&ControlObject::valueChanged,
this,
&CueControl::setHotcueIndicesSortedByPositionCompress,
Qt::DirectConnection);
}

CueControl::~CueControl() {
Expand Down Expand Up @@ -448,6 +462,18 @@ void CueControl::passthroughChanged(double enabled) {
}
}

void CueControl::setHotcueIndicesSortedByPosition(double v) {
if (v > 0 && m_pLoadedTrack) {
m_pLoadedTrack->setHotcueIndicesSortedByPosition(HotcueSortMode::KeepOffsets);
}
}

void CueControl::setHotcueIndicesSortedByPositionCompress(double v) {
if (v > 0 && m_pLoadedTrack) {
m_pLoadedTrack->setHotcueIndicesSortedByPosition(HotcueSortMode::RemoveOffsets);
}
}

void CueControl::attachCue(const CuePointer& pCue, HotcueControl* pControl) {
VERIFY_OR_DEBUG_ASSERT(pControl) {
return;
Expand Down
6 changes: 6 additions & 0 deletions src/engine/controls/cuecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ class CueControl : public EngineControl {

void passthroughChanged(double v);

void setHotcueIndicesSortedByPosition(double v);
void setHotcueIndicesSortedByPositionCompress(double v);

void cueSet(double v);
void cueClear(double v);
void cueGoto(double v);
Expand Down Expand Up @@ -362,6 +365,9 @@ class CueControl : public EngineControl {

parented_ptr<ControlProxy> m_pPassthrough;

std::unique_ptr<ControlPushButton> m_pSortHotcuesByPos;
std::unique_ptr<ControlPushButton> m_pSortHotcuesByPosCompress;

QAtomicPointer<HotcueControl> m_pCurrentSavedLoopControl;

// Tells us which controls map to which hotcue
Expand Down
57 changes: 57 additions & 0 deletions src/track/track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,63 @@ void Track::shiftBeatsMillis(double milliseconds) {
}
}

void Track::setHotcueIndicesSortedByPosition(HotcueSortMode sortMode) {
auto locked = lockMutex(&m_qMutex);

// Populate lists of positions and indices
QList<int> indices;
QList<mixxx::audio::FramePos> positions;
indices.reserve(m_cuePoints.size());
positions.reserve(m_cuePoints.size());
for (const CuePointer& pCue : std::as_const(m_cuePoints)) {
if (pCue->getType() != mixxx::CueType::HotCue) {
continue;
}
const auto pos = pCue->getPosition();
positions.append(pos);
if (sortMode == HotcueSortMode::KeepOffsets) {
// We shall keep empty hotcues (start offset, gaps), so we need
// to store the indices
indices.append(pCue->getHotCue());
}
}

std::sort(positions.begin(), positions.end());
if (sortMode == HotcueSortMode::KeepOffsets) {
DEBUG_ASSERT(positions.size() == indices.size());
std::sort(indices.begin(), indices.end());
}

// The actual sorting:
// re-map hotcue positions to indices in ascending order
QHash<mixxx::audio::FramePos, int> posIndexHash;
if (sortMode == HotcueSortMode::RemoveOffsets) {
// Assign new indices, start with 0
int index = mixxx::kFirstHotCueIndex;
for (int i = 0; i < positions.size(); i++) {
posIndexHash.insert(positions[i], index);
index++;
}
} else { // HotcueSortMode::KeepOffsets
// Assign sorted indices
for (int i = 0; i < positions.size(); i++) {
posIndexHash.insert(positions[i], indices[i]);
}
}

// Finally set new indices on hotcues
for (CuePointer& pCue : m_cuePoints) {
if (pCue->getType() != mixxx::CueType::HotCue) {
continue;
}
int newIndex = posIndexHash.take(pCue->getPosition());
pCue->setHotCue(newIndex);
}

markDirtyAndUnlock(&locked);
emit cuesUpdated();
}

void Track::analysisFinished() {
emit analyzed();
}
Expand Down
5 changes: 5 additions & 0 deletions src/track/track.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ class Track : public QObject {
/// Shift beatgrid by a constant offset
void shiftBeatsMillis(double milliseconds);

/// Set hoctues' indices sorted by their frame position.
/// If compress is true, indices are consecutive and start at 0.
/// Set false to sort only, ie. keep empty hotcues before and in between.
void setHotcueIndicesSortedByPosition(HotcueSortMode sortMode);

// Call when analysis is done.
void analysisFinished();

Expand Down
5 changes: 5 additions & 0 deletions src/track/track_decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ enum class ExportTrackMetadataResult {
Skipped,
};

enum class HotcueSortMode {
KeepOffsets,
RemoveOffsets,
};

// key for control to open/close the decks' track menus
const QString kShowTrackMenuKey = QStringLiteral("show_track_menu");

Expand Down

0 comments on commit 2b82059

Please sign in to comment.