From 7fed042f29a1ed8b1f2ea62ccd1bc3f2c9bf2cf1 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 29 Mar 2022 01:52:59 +0200 Subject: [PATCH 1/9] Spinback: fix infinite acceleration --- src/controllers/controllerengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 187ee5263d2..25d330edf19 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -1503,8 +1503,8 @@ void ControllerEngine::softTakeoverIgnoreNextValue( Output: - -------- ------------------------------------------------------ */ void ControllerEngine::spinback(int deck, bool activate, double factor, double rate) { - // defaults for args set in header file - brake(deck, activate, factor, rate); + qDebug() << " init spinback"; + brake(deck, activate, -factor, rate); } /* -------- ------------------------------------------------------ From 87c23c210004d531f8c425bd27de59248f2e8af6 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Mon, 28 Mar 2022 15:00:47 +0200 Subject: [PATCH 2/9] Brake, spinback, softStart: add debug output --- src/controllers/controllerengine.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 25d330edf19..0a47cc0728a 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -1349,15 +1349,19 @@ void ControllerEngine::scratchProcess(int timerId) { // If we're ramping to end scratching and the wheel hasn't been turned very // recently (spinback after lift-off,) feed fixed data + qDebug() << "."; if (m_ramp[deck] && !m_softStartActive[deck] && ((mixxx::Time::elapsed() - m_lastMovement[deck]) >= mixxx::Duration::fromMillis(1))) { + qDebug() << " ramp && !softStart"; filter->observation(m_rampTo[deck] * m_rampFactor[deck]); // Once this code path is run, latch so it always runs until reset //m_lastMovement[deck] += mixxx::Duration::fromSeconds(1); } else if (m_softStartActive[deck]) { + qDebug() << " softStart"; // pretend we have moved by (desired rate*default distance) - filter->observation(m_rampTo[deck]*kAlphaBetaDt); + filter->observation(m_rampTo[deck] * kAlphaBetaDt); } else { + qDebug() << " else"; // This will (and should) be 0 if no net ticks have been accumulated // (i.e. the wheel is stopped) filter->observation(m_dx[deck] * m_intervalAccumulator[deck]); @@ -1375,6 +1379,10 @@ void ControllerEngine::scratchProcess(int timerId) { // Reset accumulator m_intervalAccumulator[deck] = 0; + qDebug() << " old " << oldRate; + qDebug() << " new " << newRate; + qDebug() << " fabs" << fabs(trunc((m_rampTo[deck] - newRate) * 100000) / 100000); + qDebug() << "."; // End scratching if we're ramping and the current rate is really close to the rampTo value if ((m_ramp[deck] && fabs(m_rampTo[deck] - newRate) <= 0.00001) || // or if we brake or softStart and have crossed over the desired value, @@ -1410,6 +1418,8 @@ void ControllerEngine::scratchProcess(int timerId) { m_dx[deck] = 0.0; m_brakeActive[deck] = false; m_softStartActive[deck] = false; + qDebug() << " DONE scratching"; + qDebug() << "."; } } @@ -1514,6 +1524,7 @@ void ControllerEngine::spinback(int deck, bool activate, double factor, double r Output: - -------- ------------------------------------------------------ */ void ControllerEngine::brake(int deck, bool activate, double factor, double rate) { + qDebug() << " init brake"; // PlayerManager::groupForDeck is 0-indexed. QString group = PlayerManager::groupForDeck(deck - 1); @@ -1582,6 +1593,7 @@ void ControllerEngine::brake(int deck, bool activate, double factor, double rate Output: - -------- ------------------------------------------------------ */ void ControllerEngine::softStart(int deck, bool activate, double factor) { + qDebug() << " init softStart"; // PlayerManager::groupForDeck is 0-indexed. QString group = PlayerManager::groupForDeck(deck - 1); From c771b2810aed7272218b01e35e59ccbc526c6569 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Mon, 28 Mar 2022 15:02:00 +0200 Subject: [PATCH 3/9] Brake, softStart: early return --- src/controllers/controllerengine.cpp | 150 ++++++++++++++------------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 0a47cc0728a..9f40827eb71 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -1541,50 +1541,52 @@ void ControllerEngine::brake(int deck, bool activate, double factor, double rate // used in scratchProcess for the different timer behavior we need m_brakeActive[deck] = activate; - double initRate = rate; - - if (activate) { - // store the new values for this spinback/brake effect - if (initRate == 1.0) {// then rate is really 1.0 or was set to default - // in /res/common-controller-scripts.js so check for real value, - // taking pitch into account - initRate = getDeckRate(group); - } - // stop ramping at a rate which doesn't produce any audible output anymore - m_rampTo[deck] = 0.01; - // if we are currently softStart()ing, stop it - if (m_softStartActive[deck]) { - m_softStartActive[deck] = false; - AlphaBetaFilter* filter = m_scratchFilters[deck]; - if (filter != nullptr) { - initRate = filter->predictedVelocity(); - } - } - - // setup timer and set scratch2 - timerId = startTimer(kScratchTimerMs); - m_scratchTimers[timerId] = deck; - ControlObjectScript* pScratch2 = getControlObjectScript(group, "scratch2"); - if (pScratch2 != nullptr) { - pScratch2->slotSet(initRate); - } + if (!activate) { + return; + } - // setup the filter with default alpha and beta*factor - double alphaBrake = 1.0/512; - // avoid decimals for fine adjusting - if (factor>1) { - factor = ((factor-1)/10)+1; - } - double betaBrake = ((1.0/512)/1024)*factor; // default*factor + double initRate = rate; + // store the new values for this spinback/brake effect + if (initRate == 1.0) { // then rate is really 1.0 or was set to default + // in /res/common-controller-scripts.js so check for real value, + // taking pitch into account + initRate = getDeckRate(group); + } + // stop ramping at a rate which doesn't produce any audible output anymore + m_rampTo[deck] = 0.01; + // if we are currently softStart()ing, stop it + if (m_softStartActive[deck]) { + m_softStartActive[deck] = false; AlphaBetaFilter* filter = m_scratchFilters[deck]; if (filter != nullptr) { - filter->init(kAlphaBetaDt, initRate, alphaBrake, betaBrake); + initRate = filter->predictedVelocity(); } + } + + // setup timer and set scratch2 + timerId = startTimer(kScratchTimerMs); + m_scratchTimers[timerId] = deck; + + ControlObjectScript* pScratch2 = getControlObjectScript(group, "scratch2"); + if (pScratch2 != nullptr) { + pScratch2->slotSet(initRate); + } - // activate the ramping in scratchProcess() - m_ramp[deck] = true; + // setup the filter with default alpha and beta*factor + double alphaBrake = 1.0 / 512; + // avoid decimals for fine adjusting + if (factor > 1) { + factor = ((factor - 1) / 10) + 1; } + double betaBrake = ((1.0 / 512) / 1024) * factor; // default*factor + AlphaBetaFilter* filter = m_scratchFilters[deck]; + if (filter != nullptr) { + filter->init(kAlphaBetaDt, initRate, alphaBrake, betaBrake); + } + + // activate the ramping in scratchProcess() + m_ramp[deck] = true; } /* -------- ------------------------------------------------------ @@ -1610,49 +1612,51 @@ void ControllerEngine::softStart(int deck, bool activate, double factor) { // used in scratchProcess for the different timer behavior we need m_softStartActive[deck] = activate; - double initRate = 0.0; - - if (activate) { - // acquire deck rate - m_rampTo[deck] = getDeckRate(group); - // if brake()ing, get current rate from filter - if (m_brakeActive[deck]) { - m_brakeActive[deck] = false; + if (!activate) { + return; + } - AlphaBetaFilter* filter = m_scratchFilters[deck]; - if (filter != nullptr) { - initRate = filter->predictedVelocity(); - } - } + double initRate = 0.0; + // acquire deck rate + m_rampTo[deck] = getDeckRate(group); - // setup timer, start playing and set scratch2 - timerId = startTimer(kScratchTimerMs); - m_scratchTimers[timerId] = deck; + // if brake()ing, get current rate from filter + if (m_brakeActive[deck]) { + m_brakeActive[deck] = false; - ControlObjectScript* pPlay = getControlObjectScript(group, "play"); - if (pPlay != nullptr) { - pPlay->slotSet(1.0); + AlphaBetaFilter* filter = m_scratchFilters[deck]; + if (filter != nullptr) { + initRate = filter->predictedVelocity(); } + } - ControlObjectScript* pScratch2 = getControlObjectScript(group, "scratch2"); - if (pScratch2 != nullptr) { - pScratch2->slotSet(initRate); - } + // setup timer, start playing and set scratch2 + timerId = startTimer(kScratchTimerMs); + m_scratchTimers[timerId] = deck; - // setup the filter like in brake(), with default alpha and beta*factor - double alphaSoft = 1.0/512; - // avoid decimals for fine adjusting - if (factor>1) { - factor = ((factor-1)/10)+1; - } - double betaSoft = ((1.0/512)/1024)*factor; // default: (1.0/512)/1024 - AlphaBetaFilter* filter = m_scratchFilters[deck]; - if (filter != nullptr) { // kAlphaBetaDt = 1/1000 seconds - filter->init(kAlphaBetaDt, initRate, alphaSoft, betaSoft); - } + ControlObjectScript* pPlay = getControlObjectScript(group, "play"); + if (pPlay != nullptr) { + pPlay->slotSet(1.0); + } - // activate the ramping in scratchProcess() - m_ramp[deck] = true; + ControlObjectScript* pScratch2 = getControlObjectScript(group, "scratch2"); + if (pScratch2 != nullptr) { + pScratch2->slotSet(initRate); } + + // setup the filter like in brake(), with default alpha and beta*factor + double alphaSoft = 1.0 / 512; + // avoid decimals for fine adjusting + if (factor > 1) { + factor = ((factor - 1) / 10) + 1; + } + double betaSoft = ((1.0 / 512) / 1024) * factor; // default: (1.0/512)/1024 + AlphaBetaFilter* filter = m_scratchFilters[deck]; + if (filter != nullptr) { // kAlphaBetaDt = 1/1000 seconds + filter->init(kAlphaBetaDt, initRate, alphaSoft, betaSoft); + } + + // activate the ramping in scratchProcess() + m_ramp[deck] = true; } From a30d4f79d25b1af41be8a33bd079d31f68e1beb4 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 29 Mar 2022 19:43:22 +0200 Subject: [PATCH 4/9] Brake: use const rampTo value --- src/controllers/controllerengine.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 9f40827eb71..8299df56078 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -30,6 +30,8 @@ constexpr int kDecks = 16; // timer. constexpr int kScratchTimerMs = 1; constexpr double kAlphaBetaDt = kScratchTimerMs / 1000.0; +// stop ramping at a rate which doesn't produce any audible output anymore +constexpr double kBrakeRampToRate = 0.01; } // namespace ControllerEngine::ControllerEngine( @@ -1555,7 +1557,9 @@ void ControllerEngine::brake(int deck, bool activate, double factor, double rate } // stop ramping at a rate which doesn't produce any audible output anymore m_rampTo[deck] = 0.01; - // if we are currently softStart()ing, stop it + m_rampTo[deck] = kBrakeRampToRate; + + // If we want to brake or spin back and are currently softStart'ing, stop it. if (m_softStartActive[deck]) { m_softStartActive[deck] = false; AlphaBetaFilter* filter = m_scratchFilters[deck]; From e0681a5e24cf9dc129b728fdf0c564ac4a49559a Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 29 Mar 2022 22:24:13 +0200 Subject: [PATCH 5/9] Scratching: handle spinback independently in brake() & scratchProcess() * allow spinback when deck is stopped * use -kBrakeRampToRate for spinback to avoid long, inaudible run out * don't allow brake to interrupt spinback * add stopScratchTimer() to remove redundant code --- src/controllers/controllerengine.cpp | 108 +++++++++++++++++---------- src/controllers/controllerengine.h | 3 +- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 8299df56078..40542a41d85 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -53,6 +53,7 @@ ControllerEngine::ControllerEngine( m_scratchFilters.resize(kDecks); m_rampFactor.resize(kDecks); m_brakeActive.resize(kDecks); + m_spinbackActive.resize(kDecks); m_softStartActive.resize(kDecks); // Initialize arrays used for testing and pointers for (int i = 0; i < kDecks; ++i) { @@ -1162,7 +1163,7 @@ int ControllerEngine::beginTimer(int interval, const QScriptValue& timerCallback Input: ID of timer to stop Output: - -------- ------------------------------------------------------ */ -void ControllerEngine::stopTimer(int timerId) { +void ControllerEngine::stopTimer(const int timerId) { if (!m_timers.contains(timerId)) { qWarning() << "Killing timer" << timerId << ": That timer does not exist!"; return; @@ -1172,6 +1173,16 @@ void ControllerEngine::stopTimer(int timerId) { m_timers.remove(timerId); } +void ControllerEngine::stopScratchTimer(const int timerId) { + if (!m_scratchTimers.contains(timerId)) { + qWarning() << "Killing scratch timer" << timerId << ": That timer does not exist!"; + return; + } + controllerDebug("Killing scratch timer:" << timerId); + killTimer(timerId); + m_scratchTimers.remove(timerId); +} + void ControllerEngine::stopAllTimers() { QMutableHashIterator i(m_timers); while (i.hasNext()) { @@ -1336,6 +1347,7 @@ void ControllerEngine::scratchTick(int deck, int interval) { Output: - -------- ------------------------------------------------------ */ void ControllerEngine::scratchProcess(int timerId) { + // TODO(ronso0) Refuse scratching if no track is loaded int deck = m_scratchTimers[timerId]; // PlayerManager::groupForDeck is 0-indexed. QString group = PlayerManager::groupForDeck(deck - 1); @@ -1387,17 +1399,16 @@ void ControllerEngine::scratchProcess(int timerId) { qDebug() << "."; // End scratching if we're ramping and the current rate is really close to the rampTo value if ((m_ramp[deck] && fabs(m_rampTo[deck] - newRate) <= 0.00001) || - // or if we brake or softStart and have crossed over the desired value, - ((m_brakeActive[deck] || m_softStartActive[deck]) && ( - (oldRate > m_rampTo[deck] && newRate < m_rampTo[deck]) || - (oldRate < m_rampTo[deck] && newRate > m_rampTo[deck]))) || - // or if the deck was stopped manually during brake or softStart - ((m_brakeActive[deck] || m_softStartActive[deck]) && (!isDeckPlaying(group)))) { + // or if we brake, spin back or softstart and have crossed over the desired value, + (m_brakeActive[deck] && newRate < m_rampTo[deck]) || + ((m_spinbackActive[deck] || m_softStartActive[deck]) && newRate > m_rampTo[deck]) || + // or if the deck was stopped manually during brake or softStart + ((m_brakeActive[deck] || m_softStartActive[deck]) && (!isDeckPlaying(group)))) { // Not ramping no mo' m_ramp[deck] = false; - if (m_brakeActive[deck]) { - // If in brake mode, set scratch2 rate to 0 and turn off the play button. + if (m_brakeActive[deck] || m_spinbackActive[deck]) { + // If in brake mode, set scratch2 rate to 0 and stop the deck. pScratch2->slotSet(0.0); ControlObjectScript* pPlay = getControlObjectScript(group, "play"); if (pPlay != nullptr) { @@ -1413,12 +1424,11 @@ void ControllerEngine::scratchProcess(int timerId) { } pScratch2Enable->slotSet(0); - // Remove timer - killTimer(timerId); - m_scratchTimers.remove(timerId); + stopScratchTimer(timerId); m_dx[deck] = 0.0; m_brakeActive[deck] = false; + m_spinbackActive[deck] = false; m_softStartActive[deck] = false; qDebug() << " DONE scratching"; qDebug() << "."; @@ -1529,44 +1539,62 @@ void ControllerEngine::brake(int deck, bool activate, double factor, double rate qDebug() << " init brake"; // PlayerManager::groupForDeck is 0-indexed. QString group = PlayerManager::groupForDeck(deck - 1); - - // kill timer when both enabling or disabling - int timerId = m_scratchTimers.key(deck); - killTimer(timerId); - m_scratchTimers.remove(timerId); - // enable/disable scratch2 mode ControlObjectScript* pScratch2Enable = getControlObjectScript(group, "scratch2_enable"); if (pScratch2Enable != nullptr) { pScratch2Enable->slotSet(activate ? 1 : 0); } - // used in scratchProcess for the different timer behavior we need - m_brakeActive[deck] = activate; + // Used for killing the current timer when both enabling or disabling + // Don't kill timer yet! This may be a brake init while currently spinning back + // and we don't want to interrupt that. + int timerId = m_scratchTimers.key(deck); if (!activate) { + m_brakeActive[deck] = false; + m_spinbackActive[deck] = false; + stopScratchTimer(timerId); return; } - - double initRate = rate; - // store the new values for this spinback/brake effect - if (initRate == 1.0) { // then rate is really 1.0 or was set to default - // in /res/common-controller-scripts.js so check for real value, - // taking pitch into account + // Distinguish spinback and brake. Both ramp to a very low rate to avoid a long, + // inaudible run out. For spinback that rate is negative so we don't cross 0. + // Spinback and brake also require different handling in scratchProcess. + double initRate; + if (rate < -kBrakeRampToRate) { // spinback + m_spinbackActive[deck] = true; + m_brakeActive[deck] = false; + m_rampTo[deck] = -kBrakeRampToRate; + initRate = rate; + } else if (rate > kBrakeRampToRate) { // brake + // It just doesn't make sense to allow brake to interrupt spinback or an + // already running brake process + if (m_spinbackActive[deck] || m_brakeActive[deck]) { + return; + } + m_brakeActive[deck] = true; + m_spinbackActive[deck] = false; + m_rampTo[deck] = kBrakeRampToRate; + // Let's fetch the current rate to create a seamless brake process initRate = getDeckRate(group); + // If we are currently softStart'ing adopt the current scratch rate + if (m_softStartActive[deck]) { + m_softStartActive[deck] = false; + AlphaBetaFilter* filter = m_scratchFilters[deck]; + if (filter != nullptr) { + initRate = filter->predictedVelocity(); + } + } + } else { // -kBrakeRampToRate <= rate <= kBrakeRampToRate + // This filters stopped deck and rare case of very low initial rates + m_brakeActive[deck] = false; + m_spinbackActive[deck] = false; + stopScratchTimer(timerId); + // TODO(ronso0) Stop deck as if we were braking to halt + return; } - // stop ramping at a rate which doesn't produce any audible output anymore - m_rampTo[deck] = 0.01; - m_rampTo[deck] = kBrakeRampToRate; + stopScratchTimer(timerId); // If we want to brake or spin back and are currently softStart'ing, stop it. - if (m_softStartActive[deck]) { - m_softStartActive[deck] = false; - AlphaBetaFilter* filter = m_scratchFilters[deck]; - if (filter != nullptr) { - initRate = filter->predictedVelocity(); - } - } // setup timer and set scratch2 timerId = startTimer(kScratchTimerMs); @@ -1605,8 +1633,7 @@ void ControllerEngine::softStart(int deck, bool activate, double factor) { // kill timer when both enabling or disabling int timerId = m_scratchTimers.key(deck); - killTimer(timerId); - m_scratchTimers.remove(timerId); + stopScratchTimer(timerId); // enable/disable scratch2 mode ControlObjectScript* pScratch2Enable = getControlObjectScript(group, "scratch2_enable"); @@ -1625,9 +1652,10 @@ void ControllerEngine::softStart(int deck, bool activate, double factor) { // acquire deck rate m_rampTo[deck] = getDeckRate(group); - // if brake()ing, get current rate from filter - if (m_brakeActive[deck]) { + // if braking or spinning back, get current rate from filter + if (m_brakeActive[deck] || m_spinbackActive[deck]) { m_brakeActive[deck] = false; + m_spinbackActive[deck] = false; AlphaBetaFilter* filter = m_scratchFilters[deck]; if (filter != nullptr) { diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 9caab019b7e..95da9cb9f82 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -193,6 +193,7 @@ class ControllerEngine : public QObject { // Scratching functions & variables void scratchProcess(int timerId); + void stopScratchTimer(const int timerId); bool isDeckPlaying(const QString& group); double getDeckRate(const QString& group); @@ -216,7 +217,7 @@ class ControllerEngine : public QObject { QVarLengthArray m_intervalAccumulator; QVarLengthArray m_lastMovement; QVarLengthArray m_dx, m_rampTo, m_rampFactor; - QVarLengthArray m_ramp, m_brakeActive, m_softStartActive; + QVarLengthArray m_ramp, m_brakeActive, m_spinbackActive, m_softStartActive; QVarLengthArray m_scratchFilters; QHash m_scratchTimers; QHash m_scriptWrappedFunctionCache; From 7b76d1cb7c9d9d22f69c0504274084d36de87ecb Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 29 Mar 2022 22:47:42 +0200 Subject: [PATCH 6/9] ControllerEngine: add stopDeck() --- src/controllers/controllerengine.cpp | 27 ++++++++++++++++++--------- src/controllers/controllerengine.h | 1 + 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 40542a41d85..20c6c53296d 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -1242,12 +1242,24 @@ bool ControllerEngine::isDeckPlaying(const QString& group) { ControlObjectScript* pPlay = getControlObjectScript(group, "play"); if (pPlay == nullptr) { - QString error = QString("Could not getControlObjectScript()"); - scriptErrorDialog(error, error); - return false; + QString error = QString("Could not get ControlObjectScript(%1, play)").arg(group); + scriptErrorDialog(error, error); + return false; + } + + return pPlay->toBool(); +} + +void ControllerEngine::stopDeck(const QString& group) { + ControlObjectScript* pPlay = getControlObjectScript(group, "play"); + + if (pPlay == nullptr) { + QString error = QString("Could not get ControlObjectScript(%1, play)").arg(group); + scriptErrorDialog(error, error); + return; } - return pPlay->get() > 0.0; + pPlay->set(0.0); } /* -------- ------------------------------------------------------ @@ -1410,10 +1422,7 @@ void ControllerEngine::scratchProcess(int timerId) { if (m_brakeActive[deck] || m_spinbackActive[deck]) { // If in brake mode, set scratch2 rate to 0 and stop the deck. pScratch2->slotSet(0.0); - ControlObjectScript* pPlay = getControlObjectScript(group, "play"); - if (pPlay != nullptr) { - pPlay->slotSet(0.0); - } + stopDeck(group); } // Clear scratch2_enable to end scratching. @@ -1589,7 +1598,7 @@ void ControllerEngine::brake(int deck, bool activate, double factor, double rate m_brakeActive[deck] = false; m_spinbackActive[deck] = false; stopScratchTimer(timerId); - // TODO(ronso0) Stop deck as if we were braking to halt + stopDeck(group); return; } stopScratchTimer(timerId); diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 95da9cb9f82..8d752ce4fb8 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -196,6 +196,7 @@ class ControllerEngine : public QObject { void stopScratchTimer(const int timerId); bool isDeckPlaying(const QString& group); + void stopDeck(const QString& group); double getDeckRate(const QString& group); Controller* m_pController; From 02fba3e5778e9629bdbefec8d4a1b3201b77e5c0 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 29 Mar 2022 22:51:19 +0200 Subject: [PATCH 7/9] ControllerEngine: stop scratching if no track is loaded --- src/controllers/controllerengine.cpp | 17 +++++++++++++++-- src/controllers/controllerengine.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 20c6c53296d..473cfc98b91 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -1262,6 +1262,18 @@ void ControllerEngine::stopDeck(const QString& group) { pPlay->set(0.0); } +bool ControllerEngine::isTrackLoaded(const QString& group) { + ControlObjectScript* pTrackLoaded = getControlObjectScript(group, "track_loaded"); + + if (pTrackLoaded == nullptr) { + QString error = QString("Could not get ControlObjectScript(%1, track_loaded)").arg(group); + scriptErrorDialog(error, error); + return false; + } + + return pTrackLoaded->toBool(); +} + /* -------- ------------------------------------------------------ Purpose: Enables scratching for relative controls Input: Virtual deck to scratch, @@ -1359,7 +1371,6 @@ void ControllerEngine::scratchTick(int deck, int interval) { Output: - -------- ------------------------------------------------------ */ void ControllerEngine::scratchProcess(int timerId) { - // TODO(ronso0) Refuse scratching if no track is loaded int deck = m_scratchTimers[timerId]; // PlayerManager::groupForDeck is 0-indexed. QString group = PlayerManager::groupForDeck(deck - 1); @@ -1415,7 +1426,9 @@ void ControllerEngine::scratchProcess(int timerId) { (m_brakeActive[deck] && newRate < m_rampTo[deck]) || ((m_spinbackActive[deck] || m_softStartActive[deck]) && newRate > m_rampTo[deck]) || // or if the deck was stopped manually during brake or softStart - ((m_brakeActive[deck] || m_softStartActive[deck]) && (!isDeckPlaying(group)))) { + ((m_brakeActive[deck] || m_softStartActive[deck]) && (!isDeckPlaying(group))) || + // or if there is no track loaded (anymore) + !isTrackLoaded(group)) { // Not ramping no mo' m_ramp[deck] = false; diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 8d752ce4fb8..92e6a9c561f 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -197,6 +197,7 @@ class ControllerEngine : public QObject { bool isDeckPlaying(const QString& group); void stopDeck(const QString& group); + bool isTrackLoaded(const QString& group); double getDeckRate(const QString& group); Controller* m_pController; From ab419aca331ea082d798ce377839c037c39d0315 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 30 Mar 2022 22:43:57 +0200 Subject: [PATCH 8/9] ControllerEngine: more const --- src/controllers/controllerengine.cpp | 17 +++++++++-------- src/controllers/controllerengine.h | 14 ++++++++++---- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 473cfc98b91..498b37c1190 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -1370,10 +1370,10 @@ void ControllerEngine::scratchTick(int deck, int interval) { Input: ID of timer for this deck Output: - -------- ------------------------------------------------------ */ -void ControllerEngine::scratchProcess(int timerId) { - int deck = m_scratchTimers[timerId]; +void ControllerEngine::scratchProcess(const int timerId) { + const int deck = m_scratchTimers[timerId]; // PlayerManager::groupForDeck is 0-indexed. - QString group = PlayerManager::groupForDeck(deck - 1); + const QString group = PlayerManager::groupForDeck(deck - 1); AlphaBetaFilter* filter = m_scratchFilters[deck]; if (!filter) { qWarning() << "Scratch filter pointer is null on deck" << deck; @@ -1546,7 +1546,8 @@ void ControllerEngine::softTakeoverIgnoreNextValue( rate (optional) Output: - -------- ------------------------------------------------------ */ -void ControllerEngine::spinback(int deck, bool activate, double factor, double rate) { +void ControllerEngine::spinback( + const int deck, bool activate, const double factor, const double rate) { qDebug() << " init spinback"; brake(deck, activate, -factor, rate); } @@ -1557,10 +1558,10 @@ void ControllerEngine::spinback(int deck, bool activate, double factor, double r rate (optional, necessary for spinback) Output: - -------- ------------------------------------------------------ */ -void ControllerEngine::brake(int deck, bool activate, double factor, double rate) { +void ControllerEngine::brake(const int deck, bool activate, double factor, const double rate) { qDebug() << " init brake"; // PlayerManager::groupForDeck is 0-indexed. - QString group = PlayerManager::groupForDeck(deck - 1); + const QString group = PlayerManager::groupForDeck(deck - 1); // enable/disable scratch2 mode ControlObjectScript* pScratch2Enable = getControlObjectScript(group, "scratch2_enable"); if (pScratch2Enable != nullptr) { @@ -1648,10 +1649,10 @@ void ControllerEngine::brake(int deck, bool activate, double factor, double rate Input: deck, activate/deactivate, factor (optional) Output: - -------- ------------------------------------------------------ */ -void ControllerEngine::softStart(int deck, bool activate, double factor) { +void ControllerEngine::softStart(const int deck, bool activate, double factor) { qDebug() << " init softStart"; // PlayerManager::groupForDeck is 0-indexed. - QString group = PlayerManager::groupForDeck(deck - 1); + const QString group = PlayerManager::groupForDeck(deck - 1); // kill timer when both enabling or disabling int timerId = m_scratchTimers.key(deck); diff --git a/src/controllers/controllerengine.h b/src/controllers/controllerengine.h index 92e6a9c561f..291e4ce2315 100644 --- a/src/controllers/controllerengine.h +++ b/src/controllers/controllerengine.h @@ -132,9 +132,15 @@ class ControllerEngine : public QObject { Q_INVOKABLE bool isScratching(int deck); Q_INVOKABLE void softTakeover(const QString& group, const QString& name, bool set); Q_INVOKABLE void softTakeoverIgnoreNextValue(const QString& group, const QString& name); - Q_INVOKABLE void brake(int deck, bool activate, double factor=1.0, double rate=1.0); - Q_INVOKABLE void spinback(int deck, bool activate, double factor=1.8, double rate=-10.0); - Q_INVOKABLE void softStart(int deck, bool activate, double factor=1.0); + Q_INVOKABLE void brake(const int deck, + bool activate, + double factor = 1.0, + const double rate = 1.0); + Q_INVOKABLE void spinback(const int deck, + bool activate, + double factor = 1.8, + const double rate = -10.0); + Q_INVOKABLE void softStart(const int deck, bool activate, double factor = 1.0); // Handler for timers that scripts set. virtual void timerEvent(QTimerEvent *event); @@ -192,7 +198,7 @@ class ControllerEngine : public QObject { ControlObjectScript* getControlObjectScript(const QString& group, const QString& name); // Scratching functions & variables - void scratchProcess(int timerId); + void scratchProcess(const int timerId); void stopScratchTimer(const int timerId); bool isDeckPlaying(const QString& group); From 03129ddb4f904adbd72f645ab075a2b9b2e994ad Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 6 Apr 2022 00:38:10 +0200 Subject: [PATCH 9/9] Scratching: use #if SCRATCH_DEBUG_OUTPUT --- src/controllers/controllerengine.cpp | 45 +++++++++++++++++++++------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/controllers/controllerengine.cpp b/src/controllers/controllerengine.cpp index 498b37c1190..59a1ec5e25a 100644 --- a/src/controllers/controllerengine.cpp +++ b/src/controllers/controllerengine.cpp @@ -23,6 +23,8 @@ // (closure compatible version of connectControl) #include +#define SCRATCH_DEBUG_OUTPUT false + namespace { constexpr int kDecks = 16; @@ -1371,6 +1373,10 @@ void ControllerEngine::scratchTick(int deck, int interval) { Output: - -------- ------------------------------------------------------ */ void ControllerEngine::scratchProcess(const int timerId) { +#if SCRATCH_DEBUG_OUTPUT + qDebug() << " ."; + qDebug() << " ControllerEngine::scratchProcess"; +#endif const int deck = m_scratchTimers[timerId]; // PlayerManager::groupForDeck is 0-indexed. const QString group = PlayerManager::groupForDeck(deck - 1); @@ -1380,7 +1386,9 @@ void ControllerEngine::scratchProcess(const int timerId) { return; } +#if SCRATCH_DEBUG_OUTPUT const double oldRate = filter->predictedVelocity(); +#endif // Give the filter a data point: @@ -1389,16 +1397,22 @@ void ControllerEngine::scratchProcess(const int timerId) { qDebug() << "."; if (m_ramp[deck] && !m_softStartActive[deck] && ((mixxx::Time::elapsed() - m_lastMovement[deck]) >= mixxx::Duration::fromMillis(1))) { - qDebug() << " ramp && !softStart"; +#if SCRATCH_DEBUG_OUTPUT + qDebug() << " ramp && !softStart"; +#endif filter->observation(m_rampTo[deck] * m_rampFactor[deck]); // Once this code path is run, latch so it always runs until reset //m_lastMovement[deck] += mixxx::Duration::fromSeconds(1); } else if (m_softStartActive[deck]) { - qDebug() << " softStart"; +#if SCRATCH_DEBUG_OUTPUT + qDebug() << " softStart"; +#endif // pretend we have moved by (desired rate*default distance) filter->observation(m_rampTo[deck] * kAlphaBetaDt); } else { - qDebug() << " else"; +#if SCRATCH_DEBUG_OUTPUT + qDebug() << " else"; +#endif // This will (and should) be 0 if no net ticks have been accumulated // (i.e. the wheel is stopped) filter->observation(m_dx[deck] * m_intervalAccumulator[deck]); @@ -1416,10 +1430,13 @@ void ControllerEngine::scratchProcess(const int timerId) { // Reset accumulator m_intervalAccumulator[deck] = 0; - qDebug() << " old " << oldRate; - qDebug() << " new " << newRate; - qDebug() << " fabs" << fabs(trunc((m_rampTo[deck] - newRate) * 100000) / 100000); +#if SCRATCH_DEBUG_OUTPUT + qDebug() << "."; + qDebug() << " oldRate " << oldRate; + qDebug() << " newRate " << newRate; + qDebug() << " fabs " << fabs(trunc((m_rampTo[deck] - newRate) * 100000) / 100000); qDebug() << "."; +#endif // End scratching if we're ramping and the current rate is really close to the rampTo value if ((m_ramp[deck] && fabs(m_rampTo[deck] - newRate) <= 0.00001) || // or if we brake, spin back or softstart and have crossed over the desired value, @@ -1433,6 +1450,9 @@ void ControllerEngine::scratchProcess(const int timerId) { m_ramp[deck] = false; if (m_brakeActive[deck] || m_spinbackActive[deck]) { +#if SCRATCH_DEBUG_OUTPUT + qDebug() << " brake || spinback, stop scratching, stop deck"; +#endif // If in brake mode, set scratch2 rate to 0 and stop the deck. pScratch2->slotSet(0.0); stopDeck(group); @@ -1452,8 +1472,10 @@ void ControllerEngine::scratchProcess(const int timerId) { m_brakeActive[deck] = false; m_spinbackActive[deck] = false; m_softStartActive[deck] = false; +#if SCRATCH_DEBUG_OUTPUT qDebug() << " DONE scratching"; - qDebug() << "."; + qDebug() << " ."; +#endif } } @@ -1548,7 +1570,8 @@ void ControllerEngine::softTakeoverIgnoreNextValue( -------- ------------------------------------------------------ */ void ControllerEngine::spinback( const int deck, bool activate, const double factor, const double rate) { - qDebug() << " init spinback"; + qDebug() << "ControllerEngine::spinback(deck:" << deck << ", activate:" << activate + << ", factor:" << factor << ", rate:" << rate; brake(deck, activate, -factor, rate); } @@ -1559,7 +1582,8 @@ void ControllerEngine::spinback( Output: - -------- ------------------------------------------------------ */ void ControllerEngine::brake(const int deck, bool activate, double factor, const double rate) { - qDebug() << " init brake"; + qDebug() << "ControllerEngine::brake(deck:" << deck << ", activate:" << activate + << ", factor:" << factor << ", rate:" << rate; // PlayerManager::groupForDeck is 0-indexed. const QString group = PlayerManager::groupForDeck(deck - 1); // enable/disable scratch2 mode @@ -1650,7 +1674,8 @@ void ControllerEngine::brake(const int deck, bool activate, double factor, const Output: - -------- ------------------------------------------------------ */ void ControllerEngine::softStart(const int deck, bool activate, double factor) { - qDebug() << " init softStart"; + qDebug() << "ControllerEngine::softStart(deck:" << deck << ", activate:" << activate + << ", factor:" << factor; // PlayerManager::groupForDeck is 0-indexed. const QString group = PlayerManager::groupForDeck(deck - 1);