Skip to content

Commit

Permalink
Merge pull request #3698 from ywwg/synclock-explicit-rebased
Browse files Browse the repository at this point in the history
Sync Lock Explicit Leader
  • Loading branch information
daschuer committed May 14, 2021
2 parents 98d6bac + 67580bb commit 6ed6a09
Show file tree
Hide file tree
Showing 15 changed files with 956 additions and 565 deletions.
87 changes: 87 additions & 0 deletions res/skins/LateNight/classic/buttons/btn__leader_deck.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 26 additions & 1 deletion res/skins/LateNight/decks/rate_controls.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,33 @@
<SetVariable name="Size">48f,22f</SetVariable>
<SetVariable name="BtnSize">sync</SetVariable>
<SetVariable name="ConfigKey"><Variable name="Group"/>,sync_enabled</SetVariable>
<SetVariable name="ConfigKeyRight"><Variable name="Group"/>,beatsync_tempo</SetVariable>
<SetVariable name="ConfigKeyRight"><Variable name="Group"/>,sync_master</SetVariable>
</Template>
<PushButton>
<TooltipId>sync_master</TooltipId>
<ObjectName>SyncLeader</ObjectName>
<Size>48f,22f</Size>
<NumberStates>3</NumberStates>
<State>
<Number>0</Number>
<Unpressed scalemode="STRETCH">skin:/<Variable name="BtnScheme"/>/buttons/btn_<Variable name="BtnType"/>_sync.svg</Unpressed>
<Pressed scalemode="STRETCH">skin:/<Variable name="BtnScheme"/>/buttons/btn_<Variable name="BtnType"/>_sync_active.svg</Pressed>
</State>
<State>
<Number>1</Number>
<Unpressed scalemode="STRETCH">skin:/<Variable name="BtnScheme"/>/buttons/btn_<Variable name="BtnType"/>_sync_active.svg</Unpressed>
<Pressed scalemode="STRETCH">skin:/<Variable name="BtnScheme"/>/buttons/btn_<Variable name="BtnType"/>_sync_active.svg</Pressed>
</State>
<State>
<Number>2</Number>
<Unpressed scalemode="STRETCH">skin:/<Variable name="BtnScheme"/>/buttons/btn_<Variable name="BtnType"/>_sync_active.svg</Unpressed>
<Pressed scalemode="STRETCH">skin:/<Variable name="BtnScheme"/>/buttons/btn_<Variable name="BtnType"/>_sync_active.svg</Pressed>
</State>
<Connection>
<ConfigKey><Variable name="Group"/>,sync_master</ConfigKey>
<ButtonState>LeftButton</ButtonState>
</Connection>
</PushButton>
</Children>
</WidgetGroup>

Expand Down
15 changes: 14 additions & 1 deletion res/skins/LateNight/style_classic.qss
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,7 @@ WPushButton#CueDeck,
WPushButton#LoopActivate,
#RateControls WPushButton,
WPushButton#SyncDeck,
WPushButton#SyncLeader,
WPushButton#SyncSampler,
#MixerContainer WPushButton,
#FxUnitContainer WPushButton,
Expand Down Expand Up @@ -1363,6 +1364,7 @@ WPushButton#VinylModeButton[displayValue="2"],
WPushButton#MixModeButton[displayValue="1"],
#RateControls WPushButton[displayValue="0"],
WPushButton#SyncDeck[value="0"],
WPushButton#SyncLeader[value="0"],
#MixerContainer WPushButton[displayValue="0"],
#FxUnitContainer WPushButton[displayValue="0"],
#Sampler WPushButton[displayValue="0"],
Expand All @@ -1377,10 +1379,17 @@ WPushButton#SkinSettingsToggle[displayValue="0"] {
background-color: #262626;
}

/* Orange for 'partially active' status */
#SyncLeader[value="1"] {
background-color: #db7700;
}

/* Red for 'active' status */
#BeatgridControls WPushButton[value="1"],
#RateControls WPushButton[value="1"],
#SyncDeck[value="1"], #SyncSampler[displayValue="1"],
#SyncDeck[value="1"],
#SyncLeader[value="2"],
#SyncSampler[displayValue="1"],
WPushButton#PlayDeck[displayValue="1"],
WPushButton#PlayDeckMini[displayValue="1"],
WPushButton#PlaySampler[displayValue="1"],
Expand Down Expand Up @@ -1660,6 +1669,10 @@ WPushButton#SamplerExpand[displayValue="0"],
image: url(skin:/classic/buttons/btn__sync_deck.svg) no-repeat center center;
}

#SyncLeader {
image: url(skin:/classic/buttons/btn__leader_deck.svg) no-repeat center center;
}

#SyncSampler {
image: url(skin:/classic/buttons/btn__sync_sampler.svg) no-repeat center center;
}
Expand Down
72 changes: 35 additions & 37 deletions src/engine/controls/bpmcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,7 @@ double BpmControl::calcSyncedRate(double userTweak) {

// If we are not quantized, or there are no beats, or we're master,
// or we're in reverse, just return the rate as-is.
if (!m_pQuantize->toBool() || isMaster(getSyncMode()) ||
!m_pBeats || m_pReverseButton->toBool()) {
if (!m_pQuantize->toBool() || !m_pBeats || m_pReverseButton->toBool()) {
m_resetSyncAdjustment = true;
return rate + userTweak;
}
Expand Down Expand Up @@ -472,30 +471,29 @@ double BpmControl::calcSyncAdjustment(bool userTweakingSync) {
// than modular 1.0 beat fractions. This will allow sync to work across loop
// boundaries too.

double syncTargetBeatDistance = m_dSyncTargetBeatDistance.getValue();
// We want the untweaked beat distance, so we have to add the offset here.
double thisBeatDistance = m_pThisBeatDistance->get() + m_dUserOffset.getValue();
double shortest_distance = shortestPercentageChange(
syncTargetBeatDistance, thisBeatDistance);
const double syncTargetBeatDistance = m_dSyncTargetBeatDistance.getValue();
const double thisBeatDistance = m_pThisBeatDistance->get();
const double error = shortestPercentageChange(syncTargetBeatDistance, thisBeatDistance);
const double curUserOffset = m_dUserOffset.getValue();

if (kLogger.traceEnabled()) {
kLogger.trace() << m_group << "****************";
kLogger.trace() << "master beat distance:" << syncTargetBeatDistance;
kLogger.trace() << "my beat distance:" << thisBeatDistance;
kLogger.trace() << "user offset distance:" << m_dUserOffset.getValue();
kLogger.trace() << "error :"
<< (shortest_distance - m_dUserOffset.getValue());
kLogger.trace() << "user offset :" << m_dUserOffset.getValue();
kLogger.trace() << "user offset distance:" << curUserOffset;
kLogger.trace() << "error :" << error;
}

double adjustment = 1.0;

if (userTweakingSync) {
// Don't do anything else, leave it
// The user is actively adjusting the sync, so take the current error as their preferred
// user offset.
adjustment = 1.0;
m_dUserOffset.setValue(shortest_distance);
// When updating the user offset, make sure to remove the existing offset or else it
// will get double-applied.
m_dUserOffset.setValue(error + curUserOffset);
} else {
double error = shortest_distance - m_dUserOffset.getValue();
// Threshold above which we do sync adjustment.
const double kErrorThreshold = 0.01;
// Threshold above which sync is really, really bad, so much so that we
Expand Down Expand Up @@ -550,16 +548,25 @@ double BpmControl::getBeatDistance(double dThisPosition) const {
double dBeatLength = dNextBeat - dPrevBeat;
double dBeatPercentage = dBeatLength == 0.0 ? 0.0 :
(dThisPosition - dPrevBeat) / dBeatLength;
// Because findNext and findPrev have an epsilon built in, sometimes
// the beat percentage is out of range. Fix it.
dBeatPercentage -= m_dUserOffset.getValue();
// Because findNext and findPrev have an epsilon built in, and because the user
// might have tweaked the offset, sometimes the beat percentage is out of range.
// Fix it.
if (dBeatPercentage < 0) {
++dBeatPercentage;
}
if (dBeatPercentage > 1) {
--dBeatPercentage;
}

return dBeatPercentage - m_dUserOffset.getValue();
if (kLogger.traceEnabled()) {
kLogger.trace() << getGroup() << "BpmControl::getBeatDistance"
<< dBeatPercentage << "- offset "
<< m_dUserOffset.getValue() << " = "
<< (dBeatPercentage - m_dUserOffset.getValue());
}

return dBeatPercentage;
}

// static
Expand Down Expand Up @@ -625,11 +632,6 @@ double BpmControl::getNearestPositionInPhase(

SyncMode syncMode = getSyncMode();

// Explicit master buffer is always in sync!
if (syncMode == SYNC_MASTER_EXPLICIT) {
return dThisPosition;
}

// Get the current position of this deck.
double dThisPrevBeat = m_pPrevBeat->get();
double dThisNextBeat = m_pNextBeat->get();
Expand Down Expand Up @@ -785,22 +787,15 @@ double BpmControl::getBeatMatchPosition(
if (!m_pBeats) {
return dThisPosition;
}
// Explicit master buffer is always in sync!
if (getSyncMode() == SYNC_MASTER_EXPLICIT) {
return dThisPosition;
}

EngineBuffer* pOtherEngineBuffer = nullptr;
// explicit master always syncs to itself, so keep it null
if (getSyncMode() != SYNC_MASTER_EXPLICIT) {
pOtherEngineBuffer = pickSyncTarget();
if (kLogger.traceEnabled()) {
if (pOtherEngineBuffer) {
kLogger.trace() << "BpmControl::getBeatMatchPosition sync target"
<< pOtherEngineBuffer->getGroup();
} else {
kLogger.trace() << "BpmControl::getBeatMatchPosition no sync target found";
}
pOtherEngineBuffer = pickSyncTarget();
if (kLogger.traceEnabled()) {
if (pOtherEngineBuffer) {
kLogger.trace() << "BpmControl::getBeatMatchPosition sync target"
<< pOtherEngineBuffer->getGroup();
} else {
kLogger.trace() << "BpmControl::getBeatMatchPosition no sync target found";
}
}
if (playing) {
Expand Down Expand Up @@ -898,6 +893,9 @@ double BpmControl::getBeatMatchPosition(
return dThisPosition;
}

// Offset the other deck's user offset, if any.
dOtherBeatFraction -= pOtherEngineBuffer->getUserOffset();

// We can either match the past beat with dOtherBeatFraction 1.0
// or the next beat with dOtherBeatFraction 0.0
// We prefer the next because this is what will be played,
Expand Down Expand Up @@ -1083,7 +1081,7 @@ double BpmControl::updateLocalBpm() {
double BpmControl::updateBeatDistance() {
double beatDistance = getBeatDistance(getSampleOfTrack().current);
m_pThisBeatDistance->set(beatDistance);
if (!isSynchronized()) {
if (!isSynchronized() && m_dUserOffset.getValue() != 0.0) {
m_dUserOffset.setValue(0.0);
}
if (kLogger.traceEnabled()) {
Expand Down
8 changes: 7 additions & 1 deletion src/engine/controls/bpmcontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class BpmControl : public EngineControl {
/// getBeatDistance is adjusted to include the user offset so it's
/// transparent to other decks.
double getBeatDistance(double dThisPosition) const;
double getUserOffset() const {
return m_dUserOffset.getValue();
}

void setTargetBeatDistance(double beatDistance);
void setInstantaneousBpm(double instantaneousBpm);
Expand Down Expand Up @@ -153,6 +156,9 @@ class BpmControl : public EngineControl {

ControlProxy* m_pThisBeatDistance;
ControlValueAtomic<double> m_dSyncTargetBeatDistance;
// The user offset is a beat distance percentage value that the user has tweaked a deck
// to bring it in sync with the other decks. This value is added to the reported beat
// distance to get the virtual beat distance used for sync.
ControlValueAtomic<double> m_dUserOffset;
QAtomicInt m_resetSyncAdjustment;
ControlProxy* m_pSyncMode;
Expand All @@ -166,6 +172,6 @@ class BpmControl : public EngineControl {
// m_pBeats is written from an engine worker thread
mixxx::BeatsPointer m_pBeats;

FRIEND_TEST(EngineSyncTest, UserTweakBeatDistance);
FRIEND_TEST(EngineSyncTest, UserTweakPreservedInSeek);
FRIEND_TEST(EngineSyncTest, FollowerUserTweakPreservedInMasterChange);
};
2 changes: 1 addition & 1 deletion src/engine/controls/ratecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ double RateControl::calculateSpeed(double baserate, double speed, bool paused,
*pReportScratching = true;
} else {
// If master sync is on, respond to it -- but vinyl and scratch mode always override.
if (isFollower(getSyncMode()) && !paused &&
if (toSynchronized(getSyncMode()) && !paused &&
!bVinylControlEnabled && !useScratch2Value) {
if (m_pBpmControl == nullptr) {
qDebug() << "ERROR: calculateRate m_pBpmControl is null during master sync";
Expand Down
Loading

0 comments on commit 6ed6a09

Please sign in to comment.