Skip to content

Commit

Permalink
Add reverse jump and make jump quantized
Browse files Browse the repository at this point in the history
  • Loading branch information
acolombier committed Feb 3, 2025
1 parent 4582733 commit 10a5ca9
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 12 deletions.
110 changes: 98 additions & 12 deletions src/engine/controls/cuecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "moc_cuecontrol.cpp"
#include "preferences/colorpalettesettings.h"
#include "track/track.h"
#include "util/assert.h"
#include "util/color/predefinedcolorpalettes.h"
#include "vinylcontrol/defs_vinylcontrol.h"

Expand Down Expand Up @@ -98,7 +99,8 @@ CueControl::CueControl(const QString& group,
m_bypassCueSetByPlay(false),
m_iNumHotCues(NUM_HOT_CUES),
m_pCurrentSavedLoopControl(nullptr),
m_trackMutex(QT_RECURSIVE_MUTEX_INIT) {
m_trackMutex(QT_RECURSIVE_MUTEX_INIT),
m_pBeats(nullptr) {
// To silence a compiler warning about CUE_MODE_PIONEER.
Q_UNUSED(CUE_MODE_PIONEER);

Expand Down Expand Up @@ -139,7 +141,6 @@ CueControl::~CueControl() {
void CueControl::process(const double rate,
mixxx::audio::FramePos currentPosition,
const std::size_t bufferSize) {
Q_UNUSED(rate);
Q_UNUSED(bufferSize);
for (const auto& pControl : std::as_const(m_hotcueControls)) {
if (!pControl->getCue() ||
Expand All @@ -148,23 +149,107 @@ void CueControl::process(const double rate,
!pControl->getEndPosition().isValid()) {
continue;
}
// Saved jumps store the position to jump from as their end position
if (pControl->getEndPosition() > m_lastProcessedPosition &&
if (rate >= 0
// Saved jumps store the position to jump from as their end position
&& pControl->getEndPosition() > m_lastProcessedPosition &&
pControl->getEndPosition() <= currentPosition) {
auto delta = pControl->getEndPosition() - currentPosition;
seekAbs(pControl->getPosition() + delta);
if (pControl->getPosition() < pControl->getEndPosition()) {
// If the saved jump is backward, we make the cue idle so it
// prevent creating a fake loop
pControl->setStatus(HotcueControl::Status::Set);
}
jumpTo(currentPosition, pControl->getEndPosition(), pControl->getPosition());
} else if (rate < 0
// Saved jumps store the position to jump from as their end position
&& pControl->getPosition() < m_lastProcessedPosition &&
pControl->getPosition() >= currentPosition) {
jumpTo(currentPosition, pControl->getPosition(), pControl->getEndPosition());
} else {
continue;
}
if (pControl->getPosition() < pControl->getEndPosition()) {
// If the saved jump is backward, we make the cue idle so it
// prevent creating a fake loop
pControl->setStatus(HotcueControl::Status::Set);
}
}
m_lastProcessedPosition = currentPosition;
}

void CueControl::jumpTo(mixxx::audio::FramePos currentPosition,
mixxx::audio::FramePos source,
mixxx::audio::FramePos target) {
if (m_pQuantizeEnabled->toBool()) {
VERIFY_OR_DEBUG_ASSERT(m_pBeats != nullptr) {
// FIXME early return or default to no quantizing?
return;
}
// Assuming the following unaligned jump
// |.....[|......|......|...]..|......|
// When quantize is enabled, the target is adjusted to match a beat rounded jump
// |.....[!......|......|.....]!......|
// The closest beat might be ahead of play position.
mixxx::audio::FramePos prevBeatPosition;
mixxx::audio::FramePos nextBeatPosition;
if (!m_pBeats->findPrevNextBeats(source,
&prevBeatPosition,
&nextBeatPosition,
false)) {
// FIXME early return or default to no quantizing?
return;
}

const mixxx::audio::FramePos closestSourceBeatPosition =
(nextBeatPosition - source >
source - prevBeatPosition)
? prevBeatPosition
: nextBeatPosition;

if (!m_pBeats->findPrevNextBeats(target,
&prevBeatPosition,
&nextBeatPosition,
false)) {
// FIXME early return or default to no quantizing?
return;
}

const mixxx::audio::FramePos closestTargetBeatPosition =
(nextBeatPosition - target >
target - prevBeatPosition)
? prevBeatPosition
: nextBeatPosition;

auto delta = closestSourceBeatPosition - currentPosition;
m_lastProcessedPosition = mixxx::audio::FramePos();
m_jumpCueSeekRequest = closestTargetBeatPosition + delta;
seekAbs(m_jumpCueSeekRequest);
return;
}
auto delta = source - currentPosition;
m_jumpCueSeekRequest = target + delta;
seekAbs(m_jumpCueSeekRequest);
}

void CueControl::notifySeek(mixxx::audio::FramePos position) {
m_lastProcessedPosition = position;

qDebug() << m_jumpCueSeekRequest << position;
if (m_jumpCueSeekRequest == position) {
m_jumpCueSeekRequest = mixxx::audio::FramePos();
return;
}

for (const auto& pControl : std::as_const(m_hotcueControls)) {
if (!pControl->getCue() ||
pControl->getStatus() != HotcueControl::Status::Active ||
pControl->getCue()->getType() != mixxx::CueType::Jump ||
!pControl->getEndPosition().isValid()) {
continue;
}
if ((pControl->getPosition() < pControl->getEndPosition() &&
pControl->getPosition() <= position &&
pControl->getEndPosition() > position) ||
(pControl->getPosition() > pControl->getEndPosition() &&
pControl->getPosition() > position &&
pControl->getEndPosition() <= position)) {
pControl->setStatus(HotcueControl::Status::Set);
}
}
}

void CueControl::createControls() {
Expand Down Expand Up @@ -542,6 +627,7 @@ void CueControl::trackLoaded(TrackPointer pNewTrack) {
return;
}
m_pLoadedTrack = pNewTrack;
trackBeatsUpdated(pNewTrack->getBeats());

connect(m_pLoadedTrack.get(),
&Track::analyzed,
Expand Down Expand Up @@ -830,7 +916,7 @@ void CueControl::trackCuesUpdated() {
}

void CueControl::trackBeatsUpdated(mixxx::BeatsPointer pBeats) {
Q_UNUSED(pBeats);
m_pBeats = pBeats;
loadCuesFromTrack();
}

Expand Down
8 changes: 8 additions & 0 deletions src/engine/controls/cuecontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ class CueControl : public EngineControl {
int getHotcueFocusIndex() const;
mixxx::RgbColor colorFromConfig(const ConfigKey& configKey);

void jumpTo(mixxx::audio::FramePos currentPosition,
mixxx::audio::FramePos source,
mixxx::audio::FramePos target);

UserSettingsPointer m_pConfig;
ColorPaletteSettings m_colorPaletteSettings;
QAtomicInt m_currentlyPreviewingIndex;
Expand Down Expand Up @@ -372,6 +376,7 @@ class CueControl : public EngineControl {
QAtomicPointer<HotcueControl> m_pCurrentSavedJumpControl;

mixxx::audio::FramePos m_lastProcessedPosition;
mixxx::audio::FramePos m_jumpCueSeekRequest;

// Tells us which controls map to which hotcue
QMap<QObject*, int> m_controlMap;
Expand All @@ -380,5 +385,8 @@ class CueControl : public EngineControl {
QT_RECURSIVE_MUTEX m_trackMutex;
TrackPointer m_pLoadedTrack; // is written from an engine worker thread

// m_pBeats is written from an engine worker thread
mixxx::BeatsPointer m_pBeats;

friend class HotcueControlTest;
};

0 comments on commit 10a5ca9

Please sign in to comment.