summaryrefslogtreecommitdiffstats
path: root/src/engine/controls/loopingcontrol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/controls/loopingcontrol.cpp')
-rw-r--r--src/engine/controls/loopingcontrol.cpp228
1 files changed, 133 insertions, 95 deletions
diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp
index 4015076143..03e9a4c281 100644
--- a/src/engine/controls/loopingcontrol.cpp
+++ b/src/engine/controls/loopingcontrol.cpp
@@ -124,12 +124,6 @@ LoopingControl::LoopingControl(const QString& group,
Qt::DirectConnection);
m_pQuantizeEnabled = ControlObject::getControl(ConfigKey(group, "quantize"));
- m_pNextBeat = ControlObject::getControl(ConfigKey(group, "beat_next"));
- m_pNextBeat->setKbdRepeatable(true);
- m_pPreviousBeat = ControlObject::getControl(ConfigKey(group, "beat_prev"));
- m_pPreviousBeat->setKbdRepeatable(true);
- m_pClosestBeat = ControlObject::getControl(ConfigKey(group, "beat_closest"));
- m_pTrackSamples = ControlObject::getControl(ConfigKey(group, "track_samples"));
m_pSlipEnabled = ControlObject::getControl(ConfigKey(group, "slip_enabled"));
// DEPRECATED: Use beatloop_size and beatloop_set instead.
@@ -301,9 +295,8 @@ void LoopingControl::slotLoopScale(double scaleFactor) {
const mixxx::audio::FrameDiff_t loopLength =
(loopInfo.endPosition - loopInfo.startPosition) * scaleFactor;
- const auto trackEndPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pTrackSamples->get());
+ const FrameInfo info = frameInfo();
+ const auto trackEndPosition = info.trackEndPosition;
if (!trackEndPosition.isValid()) {
return;
}
@@ -444,64 +437,84 @@ mixxx::audio::FramePos LoopingControl::nextTrigger(bool reverse,
}
if (m_bLoopingEnabled &&
- !m_bAdjustingLoopIn && !m_bAdjustingLoopOut &&
loopInfo.startPosition.isValid() &&
loopInfo.endPosition.isValid()) {
- if (loopInfo.startPosition != m_oldLoopInfo.startPosition ||
- loopInfo.endPosition != m_oldLoopInfo.endPosition) {
- // bool seek is only valid after the loop has changed
- switch (loopInfo.seekMode) {
- case LoopSeekMode::Changed:
- // here the loop has changed and the play position
- // should be moved with it
- *pTargetPosition = adjustedPositionInsideAdjustedLoop(currentPosition,
- reverse,
- m_oldLoopInfo.startPosition,
- m_oldLoopInfo.endPosition,
- loopInfo.startPosition,
- loopInfo.endPosition);
- break;
- case LoopSeekMode::MovedOut: {
- bool movedOut = false;
- // Check if we have moved out of the loop, before we could enable it
- if (reverse) {
- if (loopInfo.startPosition > currentPosition) {
- movedOut = true;
- }
- } else {
- if (loopInfo.endPosition < currentPosition) {
- movedOut = true;
- }
- }
- if (movedOut) {
+ if (!m_bAdjustingLoopIn && !m_bAdjustingLoopOut) {
+ if (loopInfo.startPosition != m_oldLoopInfo.startPosition ||
+ loopInfo.endPosition != m_oldLoopInfo.endPosition) {
+ // bool seek is only valid after the loop has changed
+ switch (loopInfo.seekMode) {
+ case LoopSeekMode::Changed:
+ // here the loop has changed and the play position
+ // should be moved with it
*pTargetPosition = adjustedPositionInsideAdjustedLoop(currentPosition,
reverse,
- loopInfo.startPosition,
- loopInfo.endPosition,
+ m_oldLoopInfo.startPosition,
+ m_oldLoopInfo.endPosition,
loopInfo.startPosition,
loopInfo.endPosition);
+ break;
+ case LoopSeekMode::MovedOut: {
+ bool movedOut = false;
+ // Check if we have moved out of the loop, before we could enable it
+ if (reverse) {
+ if (loopInfo.startPosition > currentPosition) {
+ movedOut = true;
+ }
+ } else {
+ if (loopInfo.endPosition < currentPosition) {
+ movedOut = true;
+ }
+ }
+ if (movedOut) {
+ *pTargetPosition = adjustedPositionInsideAdjustedLoop(currentPosition,
+ reverse,
+ loopInfo.startPosition,
+ loopInfo.endPosition,
+ loopInfo.startPosition,
+ loopInfo.endPosition);
+ }
+ break;
+ }
+ case LoopSeekMode::None:
+ // Nothing to do here. This is used for enabling saved loops
+ // which we want to do without jumping to the loop start
+ // position.
+ break;
+ }
+ m_oldLoopInfo = loopInfo;
+ if (pTargetPosition->isValid()) {
+ // jump immediately
+ return currentPosition;
}
- break;
- }
- case LoopSeekMode::None:
- // Nothing to do here. This is used for enabling saved loops
- // which we want to do without jumping to the loop start
- // position.
- break;
}
- m_oldLoopInfo = loopInfo;
- if (pTargetPosition->isValid()) {
- // jump immediately
- return currentPosition;
+ if (reverse) {
+ *pTargetPosition = loopInfo.endPosition;
+ return loopInfo.startPosition;
+ } else {
+ *pTargetPosition = loopInfo.startPosition;
+ return loopInfo.endPosition;
}
- }
-
- if (reverse) {
- *pTargetPosition = loopInfo.endPosition;
- return loopInfo.startPosition;
} else {
- *pTargetPosition = loopInfo.startPosition;
- return loopInfo.endPosition;
+ // LOOP in or out button is pressed for adjusting.
+ // Jump back to loop start, when reaching the track end this
+ // prevents that the track stops outside the adjusted loop.
+ if (!reverse) {
+ if (m_bAdjustingLoopIn) {
+ // Just in case the user does not release loop-in in time.
+ *pTargetPosition = m_oldLoopInfo.startPosition;
+ return loopInfo.endPosition;
+ }
+ const FrameInfo info = frameInfo();
+ *pTargetPosition = loopInfo.startPosition;
+ return info.trackEndPosition;
+ } else {
+ if (m_bAdjustingLoopOut) {
+ // Just in case the user does not release loop-out in time.
+ *pTargetPosition = m_oldLoopInfo.endPosition;
+ return loopInfo.startPosition;
+ }
+ }
}
}
return mixxx::audio::kInvalidFramePos;
@@ -658,23 +671,33 @@ void LoopingControl::setLoopInToCurrentPosition() {
const mixxx::BeatsPointer pBeats = m_pBeats;
LoopInfo loopInfo = m_loopInfo.getValue();
mixxx::audio::FramePos quantizedBeatPosition;
- mixxx::audio::FramePos position = m_currentPosition.getValue();
+ const FrameInfo info = frameInfo();
+ // Note: currentPos can be past the end of the track, in the padded
+ // silence of the last buffer. This position might be not reachable in
+ // a future runs, depending on the buffering.
+ mixxx::audio::FramePos position = math_min(info.currentPosition, info.trackEndPosition);
if (m_pQuantizeEnabled->toBool() && pBeats) {
- const auto closestBeatPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pClosestBeat->get());
- if (m_bAdjustingLoopIn) {
- if (closestBeatPosition == m_currentPosition.getValue()) {
- quantizedBeatPosition = closestBeatPosition;
+ mixxx::audio::FramePos prevBeatPosition;
+ mixxx::audio::FramePos nextBeatPosition;
+ if (pBeats->findPrevNextBeats(position, &prevBeatPosition, &nextBeatPosition, false)) {
+ // both beat positions are valid
+ mixxx::audio::FramePos closestBeatPosition =
+ (nextBeatPosition - position > position - prevBeatPosition)
+ ? prevBeatPosition
+ : nextBeatPosition;
+ if (m_bAdjustingLoopIn) {
+ if (closestBeatPosition == position) {
+ quantizedBeatPosition = closestBeatPosition;
+ } else {
+ quantizedBeatPosition = prevBeatPosition;
+ }
} else {
- quantizedBeatPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pPreviousBeat->get());
+ if (closestBeatPosition > info.trackEndPosition) {
+ quantizedBeatPosition = prevBeatPosition;
+ } else {
+ quantizedBeatPosition = closestBeatPosition;
+ }
}
- } else {
- quantizedBeatPosition = closestBeatPosition;
- }
- if (quantizedBeatPosition.isValid()) {
position = quantizedBeatPosition;
}
}
@@ -684,6 +707,9 @@ void LoopingControl::setLoopInToCurrentPosition() {
if (loopInfo.endPosition.isValid() && loopInfo.endPosition < position) {
loopInfo.endPosition = mixxx::audio::kInvalidFramePos;
m_pCOLoopEndPosition->set(loopInfo.endPosition.toEngineSamplePosMaybeInvalid());
+ if (m_bLoopingEnabled) {
+ setLoopingEnabled(false);
+ }
}
// If we're looping and the loop-in and out points are now so close
@@ -787,23 +813,37 @@ void LoopingControl::setLoopOutToCurrentPosition() {
mixxx::BeatsPointer pBeats = m_pBeats;
LoopInfo loopInfo = m_loopInfo.getValue();
mixxx::audio::FramePos quantizedBeatPosition;
- mixxx::audio::FramePos position = m_currentPosition.getValue();
+ FrameInfo info = frameInfo();
+ // Note: currentPos can be past the end of the track, in the padded
+ // silence of the last buffer. This position might be not reachable in
+ // a future runs, depending on the buffering.
+ mixxx::audio::FramePos position = math_min(info.currentPosition, info.trackEndPosition);
if (m_pQuantizeEnabled->toBool() && pBeats) {
- const auto closestBeatPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pClosestBeat->get());
- if (m_bAdjustingLoopOut) {
- if (closestBeatPosition == m_currentPosition.getValue()) {
- quantizedBeatPosition = closestBeatPosition;
+ mixxx::audio::FramePos prevBeatPosition;
+ mixxx::audio::FramePos nextBeatPosition;
+ if (pBeats->findPrevNextBeats(position, &prevBeatPosition, &nextBeatPosition, false)) {
+ // both beat positions are valid
+ const mixxx::audio::FramePos closestBeatPosition =
+ (nextBeatPosition - position > position - prevBeatPosition)
+ ? prevBeatPosition
+ : nextBeatPosition;
+ if (m_bAdjustingLoopOut) {
+ if (closestBeatPosition == position) {
+ quantizedBeatPosition = closestBeatPosition;
+ } else {
+ if (nextBeatPosition > info.trackEndPosition) {
+ quantizedBeatPosition = prevBeatPosition;
+ } else {
+ quantizedBeatPosition = nextBeatPosition;
+ }
+ }
} else {
- quantizedBeatPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pNextBeat->get());
+ if (closestBeatPosition > info.trackEndPosition) {
+ quantizedBeatPosition = prevBeatPosition;
+ } else {
+ quantizedBeatPosition = closestBeatPosition;
+ }
}
- } else {
- quantizedBeatPosition = closestBeatPosition;
- }
- if (quantizedBeatPosition.isValid()) {
position = quantizedBeatPosition;
}
}
@@ -1351,9 +1391,8 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable
beats = minBeatSize;
}
- const auto trackEndPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pTrackSamples->get());
+ FrameInfo info = frameInfo();
+ const auto trackEndPosition = info.trackEndPosition;
const mixxx::BeatsPointer pBeats = m_pBeats;
if (!trackEndPosition.isValid() || !pBeats) {
clearActiveBeatLoop();
@@ -1367,15 +1406,15 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable
mixxx::audio::kInvalidFramePos,
LoopSeekMode::MovedOut};
LoopInfo loopInfo = m_loopInfo.getValue();
- mixxx::audio::FramePos currentPosition = m_currentPosition.getValue();
-
+ mixxx::audio::FramePos currentPosition = info.currentPosition;
// Start from the current position/closest beat and
// create the loop around X beats from there.
if (keepStartPoint) {
if (loopInfo.startPosition.isValid()) {
newloopInfo.startPosition = loopInfo.startPosition;
} else {
- newloopInfo.startPosition = currentPosition;
+ newloopInfo.startPosition =
+ math_min(info.currentPosition, info.trackEndPosition);
}
} else {
// If running reverse, move the loop one loop size to the left.
@@ -1587,8 +1626,9 @@ void LoopingControl::slotLoopMove(double beats) {
return;
}
+ FrameInfo info = frameInfo();
if (BpmControl::getBeatContext(pBeats,
- m_currentPosition.getValue(),
+ info.currentPosition,
nullptr,
nullptr,
nullptr,
@@ -1602,9 +1642,7 @@ void LoopingControl::slotLoopMove(double beats) {
// The track would stop as soon as the playhead crosses track end,
// so we don't allow moving a loop beyond end.
// https://github.com/mixxxdj/mixxx/issues/9478
- const auto trackEndPosition =
- mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(
- m_pTrackSamples->get());
+ const auto trackEndPosition = info.trackEndPosition;
if (!trackEndPosition.isValid() || newLoopEndPosition > trackEndPosition) {
return;
}