diff --git a/src/engine/controls/bpmcontrol.cpp b/src/engine/controls/bpmcontrol.cpp index cfb4b45fe795..e1d6ffddc8b4 100644 --- a/src/engine/controls/bpmcontrol.cpp +++ b/src/engine/controls/bpmcontrol.cpp @@ -812,9 +812,11 @@ double BpmControl::getBeatMatchPosition( } if (playing) { if (!pOtherEngineBuffer || pOtherEngineBuffer->getSpeed() == 0.0) { - // "this" track is playing, or just starting - // only match phase if the sync target is playing as well - // else use the previous phase of "this" track before the seek + // "this" track is playing, or just starting. + // Only match phase if the sync target is playing as well, + // otherwise use the previous phase of "this" track before the seek. + // This occurs when the DJ does a quantized seek -- we preserve + // the exact beat distance. pOtherEngineBuffer = getEngineBuffer(); } } else if (!pOtherEngineBuffer) { diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 5087c025e84b..224560593263 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -1353,14 +1353,10 @@ void EngineBuffer::slotEjectTrack(double v) { } double EngineBuffer::getExactPlayPos() { - double visualPlayPos = getVisualPlayPos(); - if (visualPlayPos > 0) { - return getVisualPlayPos() * getTrackSamples(); - } else { - // Track was just loaded and the first buffer was not processed yet. - // assume it is at 0:00 + if (!m_visualPlayPos->isValid()) { return 0.0; } + return m_visualPlayPos->getEnginePlayPos() * getTrackSamples(); } double EngineBuffer::getVisualPlayPos() { diff --git a/src/test/enginesynctest.cpp b/src/test/enginesynctest.cpp index fcdf45170367..2757cac744d2 100644 --- a/src/test/enginesynctest.cpp +++ b/src/test/enginesynctest.cpp @@ -1361,6 +1361,26 @@ TEST_F(EngineSyncTest, ZeroBPMRateAdjustIgnored) { ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get()); } +TEST_F(EngineSyncTest, BeatDistanceBeforeStart) { + // https://bugs.launchpad.net/mixxx/+bug/1930143 + // If the start position is before zero, we should still initialize the beat distance + // correctly. Unfortunately, this currently doesn't work. + + mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid( + m_pTrack1->getSampleRate(), 128, 0); + m_pTrack1->trySetBeats(pBeats1); + ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0); + ControlObject::set(ConfigKey(m_sGroup1, "playposition"), -.05); + ProcessBuffer(); + ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0); + ProcessBuffer(); + // This fraction is one buffer beyond the seek position -- indicating that + // we seeked correctly. + EXPECT_NEAR(0.49143461829176116, + ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(), + kMaxBeatDistanceEpsilon); +} + TEST_F(EngineSyncTest, ZeroLatencyRateChangeNoQuant) { // Confirm that a rate change in an explicit master is instantly communicated // to followers. diff --git a/src/waveform/visualplayposition.h b/src/waveform/visualplayposition.h index 9975adbef3b1..32854caa8b79 100644 --- a/src/waveform/visualplayposition.h +++ b/src/waveform/visualplayposition.h @@ -60,6 +60,9 @@ class VisualPlayPosition : public QObject { static void setCallbackEntryToDacSecs(double secs, const PerformanceTimer& time); void setInvalid() { m_valid = false; }; + bool isValid() const { + return m_valid; + } private slots: void slotAudioBufferSizeChanged(double sizeMs);