Skip to content

Commit

Permalink
PositionScratchCon: fix explicit seeking while scratching, eg. jump t…
Browse files Browse the repository at this point in the history
…o hotcue

adopt the seek position explicitly in order to avoid crazy seek speeds
  • Loading branch information
ronso0 committed Dec 31, 2024
1 parent 65f0d9f commit dff5e6a
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 16 deletions.
25 changes: 24 additions & 1 deletion src/engine/positionscratchcontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ constexpr double kThrowThreshold = 2.5;
// Max velocity we would like to stop in a given time period.
constexpr double kMaxVelocity = 100;
// Seconds to stop a throw at the max velocity.
// TODO make configurable, eg. to customize spinbacks with controllers
constexpr double kTimeToStop = 1.0;

} // anonymous namespace
Expand All @@ -93,6 +94,9 @@ PositionScratchController::PositionScratchController(const QString& group)
m_isScratching(false),
m_inertiaEnabled(false),
m_prevSamplePos(0),
// TODO we might as well use FramePos in order to use more convenient
// mixxx::audio::kInvalidFramePos, then convert to sample pos on the fly
m_seekSamplePos(std::numeric_limits<double>::quiet_NaN()),
m_samplePosDeltaSum(0),
m_scratchTargetDelta(0),
m_scratchStartPos(0),
Expand All @@ -102,6 +106,7 @@ PositionScratchController::PositionScratchController(const QString& group)
m_bufferSize(-1), // ?
m_dt(1),
m_callsPerDt(1),
m_callsToStop(1), // ?
m_p(1),
m_d(1),
m_f(0.4) {
Expand All @@ -123,6 +128,8 @@ void PositionScratchController::slotUpdateFilterParameters(double sampleRate) {
// lowpass filter
m_callsPerDt = static_cast<int>(ceil(kDefaultSampleInterval / m_dt));

m_callsToStop = m_dt / kTimeToStop;

// Tweak PD controller for different latencies
m_p = 0.3;
m_d = m_p / -2;
Expand Down Expand Up @@ -155,6 +162,22 @@ void PositionScratchController::process(double currentSamplePos,
slotUpdateFilterParameters(m_pMainSampleRate->get());
}

if (!util_isnan(m_seekSamplePos)) {
// If we were notified about a sekk, adopt the new position immediately.
m_seekSamplePos = std::numeric_limits<double>::quiet_NaN();
m_prevSamplePos = currentSamplePos;
m_rate = 0;
// Reset filters in a way that the system is settled.
// Set to the remaining error of a p controller // ??
m_samplePosDeltaSum = -(releaseRate / m_p) * m_callsPerDt;
m_pVelocityController->reset(-m_samplePosDeltaSum);
m_pRateIIFilter->reset(-m_samplePosDeltaSum);
m_scratchStartPos = m_pScratchPos->get();
m_scratchTargetDelta = 0;
m_moveDelay = 0;
return;
}

double scratchPosition = 0;
m_mouseSampleTime += m_dt;
if (m_mouseSampleTime >= kDefaultSampleInterval || !m_isScratching) {
Expand Down Expand Up @@ -182,7 +205,7 @@ void PositionScratchController::process(double currentSamplePos,
// kMaxVelocity * alpha ^ (# callbacks to stop in) = decayThreshold
// # callbacks = kTimeToStop / m_dt
// alpha = (decayThreshold / kMaxVelocity) ^ (m_dt / kTimeToStop)
const double kExponentialDecay = pow(decayThreshold / kMaxVelocity, m_dt / kTimeToStop);
const double kExponentialDecay = pow(decayThreshold / kMaxVelocity, m_callsToStop);

m_rate *= kExponentialDecay;

Expand Down
4 changes: 4 additions & 0 deletions src/engine/positionscratchcontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ class PositionScratchController : public QObject {

private:
const QString m_group;

std::unique_ptr<ControlObject> m_pScratchEnable;
std::unique_ptr<ControlObject> m_pScratchPos;
std::unique_ptr<ControlProxy> m_pMainSampleRate;
std::unique_ptr<VelocityController> m_pVelocityController;
std::unique_ptr<RateIIFilter> m_pRateIIFilter;

bool m_isScratching;
bool m_inertiaEnabled;
double m_prevSamplePos;
double m_seekSamplePos;
double m_samplePosDeltaSum;
double m_scratchTargetDelta;
double m_scratchStartPos;
Expand All @@ -58,6 +61,7 @@ class PositionScratchController : public QObject {

double m_dt;
double m_callsPerDt;
double m_callsToStop;

double m_p;
double m_d;
Expand Down
15 changes: 0 additions & 15 deletions src/engine/readaheadmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,22 +283,8 @@ double ReadAheadManager::getFilePlaypositionFromLog(
}

double filePlayposition = 0;
bool shouldNotifySeek = false;
while (m_readAheadLog.size() > 0 && numConsumedSamples > 0) {
ReadLogEntry& entry = m_readAheadLog.front();

// Notify EngineControls that we have taken a seek.
// Every new entry start with a seek
// (Not looping control)
if (shouldNotifySeek) {
if (m_pRateControl) {
const auto seekPosition =
mixxx::audio::FramePos::fromEngineSamplePos(
entry.virtualPlaypositionStart);
m_pRateControl->notifySeek(seekPosition);
}
}

// Advance our idea of the current virtual playposition to this
// ReadLogEntry's start position.
filePlayposition = entry.advancePlayposition(&numConsumedSamples);
Expand All @@ -307,7 +293,6 @@ double ReadAheadManager::getFilePlaypositionFromLog(
// This entry is empty now.
m_readAheadLog.pop_front();
}
shouldNotifySeek = true;
}

return filePlayposition;
Expand Down

0 comments on commit dff5e6a

Please sign in to comment.