1 #include "engine/sync/enginesync.h"
2
3 #include <QStringList>
4
5 #include "engine/channels/enginechannel.h"
6 #include "engine/enginebuffer.h"
7 #include "engine/sync/internalclock.h"
8 #include "util/assert.h"
9 #include "util/logger.h"
10
11 namespace {
12 const mixxx::Logger kLogger("EngineSync");
13 } // namespace
14
EngineSync(UserSettingsPointer pConfig)15 EngineSync::EngineSync(UserSettingsPointer pConfig)
16 : BaseSyncableListener(pConfig) {
17 }
18
~EngineSync()19 EngineSync::~EngineSync() {
20 }
21
pickMaster(Syncable * enabling_syncable)22 Syncable* EngineSync::pickMaster(Syncable* enabling_syncable) {
23 // If there is an explicit master, and it is playing, keep it.
24 if (m_pMasterSyncable && m_pMasterSyncable->getSyncMode() == SYNC_MASTER_EXPLICIT) {
25 return m_pMasterSyncable;
26 }
27
28 Syncable* first_stopped_deck = nullptr;
29 Syncable* first_playing_deck = nullptr;
30 int stopped_deck_count = 0;
31 int playing_deck_count = 0;
32
33 for (const auto& pSyncable : qAsConst(m_syncables)) {
34 if (pSyncable->getBaseBpm() <= 0.0) {
35 continue;
36 }
37
38 if (pSyncable != enabling_syncable) {
39 if (!pSyncable->getChannel()->isPrimaryDeck()) {
40 continue;
41 }
42 if (!pSyncable->isSynchronized()) {
43 continue;
44 }
45 }
46
47 if (pSyncable->isPlaying() && pSyncable->isAudible()) {
48 if (playing_deck_count == 0) {
49 first_playing_deck = pSyncable;
50 }
51 playing_deck_count++;
52 } else {
53 if (stopped_deck_count == 0) {
54 first_stopped_deck = pSyncable;
55 }
56 stopped_deck_count++;
57 }
58 }
59
60 if (playing_deck_count == 1) {
61 return first_playing_deck;
62 } else if (playing_deck_count > 1) {
63 return m_pInternalClock;
64 }
65
66 // No valid playing sync decks
67 if (stopped_deck_count == 1) {
68 return first_stopped_deck;
69 } else if (stopped_deck_count > 1) {
70 return m_pInternalClock;
71 }
72
73 // No valid stopped sync decks
74 return nullptr;
75 }
76
requestSyncMode(Syncable * pSyncable,SyncMode mode)77 void EngineSync::requestSyncMode(Syncable* pSyncable, SyncMode mode) {
78 if (kLogger.traceEnabled()) {
79 kLogger.trace() << "EngineSync::requestSyncMode" << pSyncable->getGroup() << mode;
80 }
81 // Based on the call hierarchy I don't think this is possible. (Famous last words.)
82 VERIFY_OR_DEBUG_ASSERT(pSyncable) {
83 return;
84 }
85
86 if (mode == SYNC_MASTER_EXPLICIT) {
87 // Note: we enable master unconditionally. If it has no valid
88 // tempo, the tempo of the old master remains until we know better
89 activateMaster(pSyncable, true);
90 if (pSyncable->getBaseBpm() > 0) {
91 setMasterParams(pSyncable, pSyncable->getBeatDistance(),
92 pSyncable->getBaseBpm(), pSyncable->getBpm());
93 }
94 } else if (mode == SYNC_FOLLOWER ||
95 mode == SYNC_MASTER_SOFT ||
96 pSyncable == m_pInternalClock) {
97 // Note: SYNC_MASTER_SOFT and SYNC_FOLLOWER cannot be set explicitly,
98 // they are calculated by pickMaster.
99 // Internal clock cannot be disabled, it is always listening
100 if (m_pMasterSyncable == pSyncable) {
101 // This Syncable was master before. Hand off.
102 m_pMasterSyncable = nullptr;
103 pSyncable->setSyncMode(SYNC_FOLLOWER);
104 }
105 Syncable* newMaster = pickMaster(pSyncable);
106 if (newMaster) {
107 activateMaster(newMaster, false);
108 }
109 if (pSyncable != newMaster) {
110 activateFollower(pSyncable);
111 }
112 } else {
113 deactivateSync(pSyncable);
114 }
115 checkUniquePlayingSyncable();
116 }
117
findBpmMatchTarget(Syncable * requester)118 Syncable* EngineSync::findBpmMatchTarget(Syncable* requester) {
119 Syncable* pStoppedTarget = nullptr;
120
121 for (const auto& pOtherSyncable : qAsConst(m_syncables)) {
122 if (pOtherSyncable == requester) {
123 continue;
124 }
125 // Skip non-master decks, like preview decks.
126 if (!pOtherSyncable->getChannel()->isMasterEnabled()) {
127 continue;
128 }
129 if (!pOtherSyncable->getChannel()->isPrimaryDeck()) {
130 continue;
131 }
132 if (pOtherSyncable->getBaseBpm() == 0.0) {
133 continue;
134 }
135
136 // If the other deck is playing we stop looking immediately. Otherwise continue looking
137 // for a playing deck with bpm > 0.0.
138 if (pOtherSyncable->isPlaying() && pOtherSyncable->isAudible()) {
139 return pOtherSyncable;
140 }
141
142 // The target is not playing. If this is the first one we have seen,
143 // record it. If we never find a playing target, we'll return
144 // this one as a fallback.
145 if (!pStoppedTarget && !requester->isPlaying()) {
146 pStoppedTarget = pOtherSyncable;
147 }
148 }
149
150 return pStoppedTarget;
151 }
152
requestEnableSync(Syncable * pSyncable,bool bEnabled)153 void EngineSync::requestEnableSync(Syncable* pSyncable, bool bEnabled) {
154 if (kLogger.traceEnabled()) {
155 kLogger.trace() << "EngineSync::requestEnableSync " << pSyncable->getGroup() << bEnabled;
156 }
157 // Sync disable request, hand off to a different function
158 if (!bEnabled) {
159 // Already disabled? Do nothing.
160 if (!pSyncable->isSynchronized()) {
161 return;
162 }
163 deactivateSync(pSyncable);
164 return;
165 }
166
167 // Already enabled? Do nothing.
168 if (pSyncable->isSynchronized()) {
169 return;
170 }
171
172 // Now go through and possible pick a new master deck if we can find one.
173 Syncable* newMaster = pickMaster(pSyncable);
174
175 // The syncable that will be used to initialize the master params, if needed
176 Syncable* pParamsSyncable = nullptr;
177
178 if (newMaster == m_pInternalClock) {
179 // This happens if we had a single master before
180 if (m_pMasterSyncable != m_pInternalClock) {
181 if (pSyncable->isPlaying()) {
182 // We are already playing, only change speed if
183 // the old master is playing as well
184 if (m_pMasterSyncable && m_pMasterSyncable->isPlaying()) {
185 // Adopt value from the old master if it is still playing
186 pParamsSyncable = m_pMasterSyncable;
187 } else {
188 DEBUG_ASSERT(pSyncable->getBaseBpm() > 0);
189 pParamsSyncable = pSyncable;
190 }
191 } else {
192 if (m_pMasterSyncable) {
193 pParamsSyncable = m_pMasterSyncable;
194 } else {
195 // Can this ever happen?
196 DEBUG_ASSERT(false);
197 pParamsSyncable = pSyncable;
198 }
199 }
200 }
201 } else if (newMaster == pSyncable) {
202 // There is no other synced deck. If any other deck is playing we will
203 // match the first available bpm -- sync won't be enabled on these decks,
204 // otherwise there would have been a master.
205 pParamsSyncable = findBpmMatchTarget(pSyncable);
206 if (pParamsSyncable == nullptr) {
207 // We weren't able to find anything to match to, so set ourselves as the
208 // target. That way we'll use our own params when we setMasterParams below.
209 pParamsSyncable = pSyncable;
210 }
211 } else if (newMaster) {
212 // This happens if this Deck has no valid BPM
213 // avoid that other decks are adjusted
214 pParamsSyncable = newMaster;
215 } else {
216 // This happens if there is no other SyncDeck around and this has no valid BPM
217 // nothing to do.
218 }
219
220 if (newMaster != nullptr && newMaster != m_pMasterSyncable) {
221 activateMaster(newMaster, false);
222 }
223
224 if (newMaster != pSyncable) {
225 pSyncable->setSyncMode(SYNC_FOLLOWER);
226 }
227
228 if (pParamsSyncable != nullptr) {
229 setMasterParams(pParamsSyncable,
230 pParamsSyncable->getBeatDistance(),
231 pParamsSyncable->getBaseBpm(),
232 pParamsSyncable->getBpm());
233 pSyncable->setInstantaneousBpm(pParamsSyncable->getBpm());
234 if (pParamsSyncable != pSyncable) {
235 pSyncable->requestSync();
236 }
237 }
238 }
239
notifyPlayingAudible(Syncable * pSyncable,bool playingAudible)240 void EngineSync::notifyPlayingAudible(Syncable* pSyncable, bool playingAudible) {
241 if (kLogger.traceEnabled()) {
242 kLogger.trace() << "EngineSync::notifyPlayingAudible"
243 << pSyncable->getGroup() << playingAudible;
244 }
245 // For now we don't care if the deck is now playing or stopping.
246 if (!pSyncable->isSynchronized()) {
247 return;
248 }
249
250 // similar to enablesync -- we pick a new master and maybe reinit.
251 Syncable* newMaster = pickMaster(playingAudible ? pSyncable : nullptr);
252
253 if (newMaster != nullptr && newMaster != m_pMasterSyncable) {
254 activateMaster(newMaster, false);
255 setMasterParams(newMaster, newMaster->getBeatDistance(), newMaster->getBaseBpm(), newMaster->getBpm());
256 }
257
258 pSyncable->requestSync();
259 }
260
notifyScratching(Syncable * pSyncable,bool scratching)261 void EngineSync::notifyScratching(Syncable* pSyncable, bool scratching) {
262 // No special behavior for now.
263 Q_UNUSED(pSyncable);
264 Q_UNUSED(scratching);
265 }
266
notifyBpmChanged(Syncable * pSyncable,double bpm)267 void EngineSync::notifyBpmChanged(Syncable* pSyncable, double bpm) {
268 if (kLogger.traceEnabled()) {
269 kLogger.trace() << "EngineSync::notifyBpmChanged" << pSyncable->getGroup() << bpm;
270 }
271
272 // Master Base BPM shouldn't be updated for every random deck that twiddles
273 // the rate.
274 setMasterBpm(pSyncable, bpm);
275 }
276
requestBpmUpdate(Syncable * pSyncable,double bpm)277 void EngineSync::requestBpmUpdate(Syncable* pSyncable, double bpm) {
278 if (kLogger.traceEnabled()) {
279 kLogger.trace() << "EngineSync::requestBpmUpdate" << pSyncable->getGroup() << bpm;
280 }
281
282 double mbaseBpm = 0.0;
283 double mbpm = 0.0;
284 double beatDistance = 0.0;
285 if (m_pMasterSyncable) {
286 mbaseBpm = m_pMasterSyncable->getBaseBpm();
287 mbpm = m_pMasterSyncable->getBpm();
288 beatDistance = m_pMasterSyncable->getBeatDistance();
289 }
290
291 if (mbaseBpm != 0.0 && mbpm != 0.0) {
292 // resync to current master
293 pSyncable->setMasterParams(beatDistance, mbaseBpm, mbpm);
294 } else {
295 // There is no other master, adopt this bpm as master
296 pSyncable->setMasterParams(0.0, 0.0, bpm);
297 }
298 }
299
notifyInstantaneousBpmChanged(Syncable * pSyncable,double bpm)300 void EngineSync::notifyInstantaneousBpmChanged(Syncable* pSyncable, double bpm) {
301 if (kLogger.traceEnabled()) {
302 kLogger.trace() << "EngineSync::notifyInstantaneousBpmChanged" << pSyncable->getGroup() << bpm;
303 }
304 if (!isMaster(pSyncable->getSyncMode())) {
305 return;
306 }
307
308 // Do not update the master rate slider because instantaneous changes are
309 // not user visible.
310 setMasterInstantaneousBpm(pSyncable, bpm);
311 }
312
notifyBeatDistanceChanged(Syncable * pSyncable,double beat_distance)313 void EngineSync::notifyBeatDistanceChanged(Syncable* pSyncable, double beat_distance) {
314 if (kLogger.traceEnabled()) {
315 kLogger.trace() << "EngineSync::notifyBeatDistanceChanged" << pSyncable->getGroup() << beat_distance;
316 }
317 if (!isMaster(pSyncable->getSyncMode())) {
318 return;
319 }
320
321 setMasterBeatDistance(pSyncable, beat_distance);
322 }
323
activateFollower(Syncable * pSyncable)324 void EngineSync::activateFollower(Syncable* pSyncable) {
325 if (pSyncable == nullptr) {
326 qWarning() << "WARNING: Logic Error: Called activateFollower on a nullptr Syncable.";
327 return;
328 }
329
330 pSyncable->setSyncMode(SYNC_FOLLOWER);
331 pSyncable->setMasterParams(masterBeatDistance(), masterBaseBpm(), masterBpm());
332 pSyncable->setInstantaneousBpm(masterBpm());
333 }
334
activateMaster(Syncable * pSyncable,bool explicitMaster)335 void EngineSync::activateMaster(Syncable* pSyncable, bool explicitMaster) {
336 VERIFY_OR_DEBUG_ASSERT(pSyncable) {
337 qWarning() << "WARNING: Logic Error: Called activateMaster on a nullptr Syncable.";
338 return;
339 }
340 if (kLogger.traceEnabled()) {
341 kLogger.trace() << "EngineSync::activateMaster: "
342 << pSyncable->getGroup() << "explicit? "
343 << explicitMaster;
344 }
345
346 if (m_pMasterSyncable == pSyncable) {
347 // Already master, update the explicit State.
348 if (explicitMaster) {
349 if (m_pMasterSyncable->getSyncMode() != SYNC_MASTER_EXPLICIT) {
350 m_pMasterSyncable->setSyncMode(SYNC_MASTER_EXPLICIT);
351 } else if (m_pMasterSyncable->getSyncMode() != SYNC_MASTER_SOFT) {
352 m_pMasterSyncable->setSyncMode(SYNC_MASTER_SOFT);
353 } else {
354 DEBUG_ASSERT(!"Logic Error: m_pMasterSyncable is a syncable that does not think it is master.");
355 }
356 }
357 // nothing else to do
358 return;
359 }
360
361 // If a channel is master, disable it.
362 Syncable* pOldChannelMaster = m_pMasterSyncable;
363
364 m_pMasterSyncable = nullptr;
365 if (pOldChannelMaster) {
366 pOldChannelMaster->setSyncMode(SYNC_FOLLOWER);
367 }
368
369 //qDebug() << "Setting up master " << pSyncable->getGroup();
370 m_pMasterSyncable = pSyncable;
371 if (explicitMaster) {
372 pSyncable->setSyncMode(SYNC_MASTER_EXPLICIT);
373 } else {
374 pSyncable->setSyncMode(SYNC_MASTER_SOFT);
375 }
376 pSyncable->setMasterParams(masterBeatDistance(), masterBaseBpm(), masterBpm());
377 pSyncable->setInstantaneousBpm(masterBpm());
378
379 if (m_pMasterSyncable != m_pInternalClock) {
380 activateFollower(m_pInternalClock);
381 }
382
383 // It is up to callers of this function to initialize bpm and beat_distance
384 // if necessary.
385 }
386
deactivateSync(Syncable * pSyncable)387 void EngineSync::deactivateSync(Syncable* pSyncable) {
388 if (kLogger.traceEnabled()) {
389 kLogger.trace() << "EngineSync::deactivateSync" << pSyncable->getGroup();
390 }
391 bool wasMaster = isMaster(pSyncable->getSyncMode());
392 if (wasMaster) {
393 m_pMasterSyncable = nullptr;
394 }
395
396 // Notifications happen after-the-fact.
397 pSyncable->setSyncMode(SYNC_NONE);
398
399 bool bSyncDeckExists = syncDeckExists();
400
401 if (pSyncable != m_pInternalClock && !bSyncDeckExists) {
402 // Deactivate the internal clock if there are no more sync decks left.
403 m_pMasterSyncable = nullptr;
404 m_pInternalClock->setSyncMode(SYNC_NONE);
405 }
406
407 Syncable* newMaster = pickMaster(nullptr);
408 if (newMaster != nullptr && m_pMasterSyncable != newMaster) {
409 activateMaster(newMaster, false);
410 }
411 }
412
pickNonSyncSyncTarget(EngineChannel * pDontPick) const413 Syncable* EngineSync::pickNonSyncSyncTarget(EngineChannel* pDontPick) const {
414 // First choice: the sync master, if it's a deck
415 if (m_pMasterSyncable &&
416 m_pMasterSyncable->getChannel() &&
417 m_pMasterSyncable->getChannel() != pDontPick) {
418 return m_pMasterSyncable;
419 }
420
421 Syncable* pFirstPlayingDeck = nullptr;
422 Syncable* pFirstNonplayingDeck = nullptr;
423 foreach (Syncable* pSyncable, m_syncables) {
424 EngineChannel* pChannel = pSyncable->getChannel();
425 // Exclude non-decks
426 if (pChannel == nullptr || pChannel == pDontPick) {
427 continue;
428 }
429
430 // Only consider channels that have a track loaded, are in the master
431 // mix, and are primary decks.
432 if (pChannel->isActive() && pChannel->isMasterEnabled() && pChannel->isPrimaryDeck()) {
433 EngineBuffer* pBuffer = pChannel->getEngineBuffer();
434 if (pBuffer && pBuffer->getBpm() > 0) {
435 if (pBuffer->getSpeed() != 0.0) {
436 if (pSyncable->getSyncMode() != SYNC_NONE) {
437 // Second choice: first playing sync deck
438 return pSyncable;
439 }
440 if (pFirstPlayingDeck == nullptr) {
441 pFirstPlayingDeck = pSyncable;
442 }
443 } else if (pFirstNonplayingDeck == nullptr) {
444 pFirstNonplayingDeck = pSyncable;
445 }
446 }
447 }
448 }
449 if (pFirstPlayingDeck) {
450 // Third choice: first playing non-sync deck
451 return pFirstPlayingDeck;
452 }
453
454 // No playing decks have a BPM. Go with the first deck that was stopped but
455 // had a BPM.
456 return pFirstNonplayingDeck;
457 }
458
otherSyncedPlaying(const QString & group)459 bool EngineSync::otherSyncedPlaying(const QString& group) {
460 bool othersInSync = false;
461 for (Syncable* theSyncable : qAsConst(m_syncables)) {
462 bool isSynchonized = theSyncable->isSynchronized();
463 if (theSyncable->getGroup() == group) {
464 if (!isSynchonized) {
465 return false;
466 }
467 continue;
468 }
469 if (theSyncable->isPlaying() && isSynchonized) {
470 othersInSync = true;
471 }
472 }
473 return othersInSync;
474 }
475