Skip to content

Commit

Permalink
Sync Lock: make sure to reinit leader params after scratching ends
Browse files Browse the repository at this point in the history
  • Loading branch information
ywwg committed Jul 19, 2021
1 parent 1a1cc2d commit feae5ec
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 22 deletions.
20 changes: 1 addition & 19 deletions src/engine/enginebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -943,18 +943,6 @@ void EngineBuffer::processTrackLocked(
// If pitch ratio and tempo ratio are equal, a linear scaler is used,
// otherwise tempo and pitch are processed individual

// If we were scratching, and scratching is over, and we're a follower,
// and we're quantized, and not paused,
// we need to sync phase or we'll be totally out of whack and the sync
// adjuster will kick in and push the track back in to sync with the
// master.
if (m_scratching_old && !is_scratching && m_pQuantize->toBool() &&
isFollower(m_pSyncControl->getSyncMode()) && !paused) {
// TODO() The resulting seek is processed in the following callback
// That is to late
requestSyncPhase();
}

double rate = 0;
// If the baserate, speed, or pitch has changed, we need to update the
// scaler. Also, if we have changed scalers then we need to update the
Expand Down Expand Up @@ -1168,12 +1156,6 @@ void EngineBuffer::process(CSAMPLE* pOutput, const int iBufferSize) {
}
#endif

if (isLeader(m_pSyncControl->getSyncMode())) {
// Report our speed to SyncControl immediately instead of waiting
// for postProcess so we can broadcast this update to followers.
m_pSyncControl->reportPlayerSpeed(m_speed_old, m_scratching_old);
}

m_iLastBufferSize = iBufferSize;
m_bCrossfadeReady = false;
}
Expand Down Expand Up @@ -1322,11 +1304,11 @@ void EngineBuffer::postProcess(const int iBufferSize) {
m_pSyncControl->setLocalBpm(localBpmValue);
m_pSyncControl->updateAudible();
SyncMode mode = m_pSyncControl->getSyncMode();
m_pSyncControl->reportPlayerSpeed(m_speed_old, m_scratching_old);
if (isLeader(mode)) {
m_pEngineSync->notifyBeatDistanceChanged(m_pSyncControl, beatDistance);
} else if (isFollower(mode)) {
// Report our speed to SyncControl. If we are master, we already did this.
m_pSyncControl->reportPlayerSpeed(m_speed_old, m_scratching_old);
m_pSyncControl->updateTargetBeatDistance();
}

Expand Down
29 changes: 26 additions & 3 deletions src/engine/sync/enginesync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,32 @@ void EngineSync::notifyPlayingAudible(Syncable* pSyncable, bool playingAudible)
}

void EngineSync::notifyScratching(Syncable* pSyncable, bool scratching) {
// No special behavior for now.
Q_UNUSED(pSyncable);
Q_UNUSED(scratching);
if (!pSyncable->isPlaying() || !pSyncable->isQuantized()) {
return;
}
// Only take action if scratching is turning off.
if (scratching) {
return;
}
qDebug() << "hello? EngineSync::notifyScratching" << pSyncable->getGroup()
<< pSyncable->getSyncMode();
if (isFollower(pSyncable->getSyncMode())) {
pSyncable->getChannel()->getEngineBuffer()->requestSyncPhase();
return;
}
if (isLeader(pSyncable->getSyncMode())) {
Syncable* pOnlyPlayer = getUniquePlayingSyncedDeck();
if (pOnlyPlayer) {
// Even if we didn't change leader, if there is only one player (us), then we should
// reinit the beat distance.
pOnlyPlayer->notifyUniquePlaying();
updateLeaderBeatDistance(pOnlyPlayer, pOnlyPlayer->getBeatDistance());
} else {
// If the Leader isn't the only player, then it will need to sync
// phase like followers do.
pSyncable->getChannel()->getEngineBuffer()->requestSyncPhase();
}
}
}

void EngineSync::notifyBaseBpmChanged(Syncable* pSyncable, double bpm) {
Expand Down
3 changes: 3 additions & 0 deletions src/engine/sync/internalclock.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class InternalClock : public QObject, public Clock, public Syncable {
bool isAudible() const override {
return false;
}
bool isQuantized() const override {
return true;
}

double getBeatDistance() const override;
void updateLeaderBeatDistance(double beatDistance) override;
Expand Down
1 change: 1 addition & 0 deletions src/engine/sync/syncable.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class Syncable {
// Only relevant for player Syncables.
virtual bool isPlaying() const = 0;
virtual bool isAudible() const = 0;
virtual bool isQuantized() const = 0;

// Gets the current speed of the syncable in bpm (bpm * rate slider), doesn't
// include scratch or FF/REW values.
Expand Down
4 changes: 4 additions & 0 deletions src/engine/sync/synccontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ bool SyncControl::isAudible() const {
return m_audible;
}

bool SyncControl::isQuantized() const {
return m_pQuantize->toBool();
}

double SyncControl::adjustSyncBeatDistance(double beatDistance) const {
// Similar to adjusting the target beat distance, when we report our beat
// distance we need to adjust it by the leader bpm adjustment factor. If
Expand Down
1 change: 1 addition & 0 deletions src/engine/sync/synccontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class SyncControl : public EngineControl, public Syncable {
void requestSync() override;
bool isPlaying() const override;
bool isAudible() const override;
bool isQuantized() const override;

double adjustSyncBeatDistance(double beatDistance) const;
double getBeatDistance() const override;
Expand Down
69 changes: 69 additions & 0 deletions src/test/enginesynctest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2646,6 +2646,75 @@ TEST_F(EngineSyncTest, SeekStayInPhase) {
EXPECT_DOUBLE_EQ(0.18925937554508981, ControlObject::get(ConfigKey(m_sGroup1, "playposition")));
}

TEST_F(EngineSyncTest, ScratchEndOtherPlayingTrackStayInPhase) {
// After scratching, confirm that we are still in phase.
// This version tests with a stopped other track.
mixxx::BeatsPointer pBeats1 =
BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(),
mixxx::Bpm(130),
mixxx::audio::kStartFramePos);
m_pTrack1->trySetBeats(pBeats1);
ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0);
ControlObject::set(ConfigKey(m_sGroup1, "sync_enabled"), 1);

mixxx::BeatsPointer pBeats2 =
BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(),
mixxx::Bpm(125),
mixxx::audio::kStartFramePos);
m_pTrack2->trySetBeats(pBeats2);
ControlObject::set(ConfigKey(m_sGroup2, "quantize"), 1.0);
ControlObject::set(ConfigKey(m_sGroup2, "sync_enabled"), 1);

ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
ProcessBuffer();
ProcessBuffer();
ControlObject::set(ConfigKey(m_sGroup1, "scratch2_enable"), 1.0);
ControlObject::set(ConfigKey(m_sGroup1, "scratch2"), 20.0);
ProcessBuffer();
ControlObject::set(ConfigKey(m_sGroup1, "scratch2_enable"), 0.0);
ProcessBuffer();
ProcessBuffer();

EXPECT_NEAR(ControlObject::get(ConfigKey(m_sInternalClockGroup, "beat_distance")),
ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
1e-8);
}

TEST_F(EngineSyncTest, ScratchEndOtherStoppedTrackStayInPhase) {
// After scratching, confirm that we are still in phase.
// This version tests with a playing other track.
mixxx::BeatsPointer pBeats1 =
BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(),
mixxx::Bpm(130),
mixxx::audio::kStartFramePos);
m_pTrack1->trySetBeats(pBeats1);
ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0);
ControlObject::set(ConfigKey(m_sGroup1, "sync_enabled"), 1);

mixxx::BeatsPointer pBeats2 =
BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(),
mixxx::Bpm(125),
mixxx::audio::kStartFramePos);
m_pTrack2->trySetBeats(pBeats2);
ControlObject::set(ConfigKey(m_sGroup2, "quantize"), 1.0);
ControlObject::set(ConfigKey(m_sGroup2, "sync_enabled"), 1);

ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
ProcessBuffer();
ProcessBuffer();
ControlObject::set(ConfigKey(m_sGroup1, "scratch2_enable"), 1.0);
ControlObject::set(ConfigKey(m_sGroup1, "scratch2"), 20.0);
ProcessBuffer();
ControlObject::set(ConfigKey(m_sGroup1, "scratch2_enable"), 0.0);
ProcessBuffer();
ProcessBuffer();

EXPECT_NEAR(ControlObject::get(ConfigKey(m_sInternalClockGroup, "beat_distance")),
ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
1e-8);
}

TEST_F(EngineSyncTest, SyncWithoutBeatgrid) {
// this tests bug lp1783020, notresetting rate when other deck has no beatgrid
mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(
Expand Down

0 comments on commit feae5ec

Please sign in to comment.