forked from mixxxdj/mixxx
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request mixxxdj#13553 from Swiftb0y/refactor/modernize-sof…
…ttakeover-code Refactor: modernize softtakeover code
- Loading branch information
Showing
17 changed files
with
454 additions
and
601 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,113 @@ | ||
#pragma once | ||
|
||
#include <QHash> | ||
#include <chrono> | ||
#include <gsl/pointers> | ||
#include <unordered_map> | ||
|
||
#include "util/duration.h" | ||
#include "util/assert.h" | ||
#include "util/time.h" | ||
|
||
class ControlObject; | ||
class ControlPotmeter; | ||
|
||
class SoftTakeover { | ||
public: | ||
// I would initialize it here but that's C++11 coolness. (Because it's a double.) | ||
static const double kDefaultTakeoverThreshold; | ||
using ClockT = mixxx::Time; | ||
// 3/128 units away from the current is enough to catch fast non-sequential moves | ||
// but not cause an audibly noticeable jump, determined experimentally with | ||
// slow-refresh controllers. | ||
// TODO (XXX): Expose this to the controller mapping environment? | ||
static constexpr double kDefaultTakeoverThreshold = 3.0 / 128; | ||
|
||
SoftTakeover(); | ||
bool ignore(ControlObject* control, double newParameter); | ||
void ignoreNext(); | ||
void setThreshold(double threshold); | ||
SoftTakeover() = default; | ||
bool ignore(const ControlObject& control, double newParameter); | ||
bool willIgnore(const ControlObject& control, | ||
double newParameter, | ||
ClockT::time_point currentTime) const; | ||
void ignoreNext() { | ||
m_time = kFirstValueTime; | ||
} | ||
void setThreshold(double threshold) { | ||
m_dThreshold = threshold; | ||
} | ||
|
||
struct TestAccess; | ||
// TODO (XXX): find a better testing solution than this TestAccess | ||
// front-door coupled to `mixxx::Time`. | ||
struct TestAccess { | ||
static constexpr ClockT::duration getTimeThreshold() { | ||
return kSubsequentValueOverrideTime; | ||
} | ||
template<class Rep = ClockT::rep, | ||
class Period = ClockT::period> | ||
static void advanceTimePastThreshold( | ||
std::chrono::duration<Rep, Period> offset = | ||
std::chrono::nanoseconds(0)) { | ||
mixxx::Time::addTestTime(getTimeThreshold() + offset); | ||
} | ||
}; | ||
|
||
private: | ||
// If a new value is received within this amount of time, jump to it | ||
// regardless. This allows quickly whipping controls to work while retaining | ||
// the benefits of soft-takeover for slower movements. Setting this too | ||
// high will defeat the purpose of soft-takeover. | ||
static const mixxx::Duration kSubsequentValueOverrideTime; | ||
static constexpr ClockT::duration kSubsequentValueOverrideTime = std::chrono::milliseconds(50); | ||
static constexpr ClockT::time_point kFirstValueTime = ClockT::time_point::min(); | ||
|
||
mixxx::Duration m_time; | ||
double m_prevParameter; | ||
double m_dThreshold; | ||
}; | ||
|
||
struct SoftTakeover::TestAccess { | ||
static mixxx::Duration getTimeThreshold() { | ||
return kSubsequentValueOverrideTime; | ||
} | ||
ClockT::time_point m_time{kFirstValueTime}; | ||
double m_prevParameter{0}; | ||
double m_dThreshold{kDefaultTakeoverThreshold}; | ||
}; | ||
|
||
class SoftTakeoverCtrl { | ||
public: | ||
SoftTakeoverCtrl(); | ||
~SoftTakeoverCtrl(); | ||
SoftTakeoverCtrl() = default; | ||
|
||
// Enable soft-takeover for the given Control. | ||
// This does nothing on a control that already has soft-takeover enabled. | ||
void enable(ControlObject* control); | ||
void enable(gsl::not_null<ControlPotmeter*> pControl); | ||
// Disable soft-takeover for the given Control | ||
void disable(ControlObject* control); | ||
void disable(ControlObject* control) { | ||
m_softTakeoverHash.erase(control); | ||
} | ||
// Check to see if the new value for the Control should be ignored | ||
bool ignore(ControlObject* control, double newMidiParameter); | ||
bool ignore(ControlObject* pControl, double newParameter) { | ||
auto it = m_softTakeoverHash.find(pControl); | ||
if (it == m_softTakeoverHash.end()) { | ||
return false; | ||
} | ||
VERIFY_OR_DEBUG_ASSERT(pControl) { | ||
return false; | ||
} | ||
auto& [coKey, refSoftTakeover] = *it; | ||
return refSoftTakeover.ignore(*pControl, newParameter); | ||
} | ||
bool willIgnore(ControlObject* pControl, double newParameter) { | ||
auto it = m_softTakeoverHash.find(pControl); | ||
if (it == m_softTakeoverHash.end()) { | ||
return false; | ||
} | ||
VERIFY_OR_DEBUG_ASSERT(pControl) { | ||
return false; | ||
} | ||
auto currentTime = SoftTakeover::ClockT::now(); | ||
auto& [coKey, refSoftTakeover] = *it; | ||
return refSoftTakeover.willIgnore(*pControl, newParameter, currentTime); | ||
} | ||
// Ignore the next supplied parameter | ||
void ignoreNext(ControlObject* control); | ||
void ignoreNext(ControlObject* pControl) { | ||
auto it = m_softTakeoverHash.find(pControl); | ||
if (it == m_softTakeoverHash.end()) { | ||
return; | ||
} | ||
auto& [coKey, refSoftTakeover] = *it; | ||
refSoftTakeover.ignoreNext(); | ||
} | ||
|
||
private: | ||
QHash<ControlObject*, SoftTakeover*> m_softTakeoverHash; | ||
// ControlObjects are borrowed. They must outlive this object. | ||
// Note that even though we can only enable softTakeover on | ||
// `ControlPotmeter`s, we store the base ControlObject to not force the user | ||
// to downcast for `disable()` and `ignore()`/`ignoreNext()`. | ||
std::unordered_map<ControlObject*, SoftTakeover> m_softTakeoverHash; | ||
}; |
Oops, something went wrong.