1 #include <gmock/gmock.h>
2 #include <gtest/gtest.h>
3 
4 #include <string>
5 
6 #include "control/controlobject.h"
7 #include "engine/controls/bpmcontrol.h"
8 #include "engine/sync/synccontrol.h"
9 #include "mixer/basetrackplayer.h"
10 #include "preferences/usersettings.h"
11 #include "test/mixxxtest.h"
12 #include "test/mockedenginebackendtest.h"
13 #include "track/beatfactory.h"
14 #include "track/beatmap.h"
15 #include "util/memory.h"
16 
17 namespace {
18 constexpr double kMaxFloatingPointErrorLowPrecision = 0.005;
19 constexpr double kMaxFloatingPointErrorHighPrecision = 0.0000000000000005;
20 constexpr double kMaxBeatDistanceEpsilon = 1e-9;
21 } // namespace
22 
23 /// Tests for Master Sync.
24 /// The following manual tests should probably be performed:
25 /// * Quantize mode nudges tracks in sync, whether internal or deck master.
26 /// * Flinging tracks with the waveform should work.
27 /// * vinyl??
28 class EngineSyncTest : public MockedEngineBackendTest {
29   public:
getMasterGroup()30     QString getMasterGroup() {
31         Syncable* pMasterSyncable = m_pEngineSync->getMasterSyncable();
32         if (pMasterSyncable) {
33             return pMasterSyncable->getGroup();
34         }
35         return QString();
36     }
isExplicitMaster(const QString & group)37     bool isExplicitMaster(const QString& group) {
38         return isMaster(group, true);
39     }
isSoftMaster(const QString & group)40     bool isSoftMaster(const QString& group) {
41         return isMaster(group, false);
42     }
43 
isFollower(const QString & group)44     bool isFollower(const QString& group) {
45         if (group == m_sInternalClockGroup) {
46             return !ControlObject::getControl(ConfigKey(m_sInternalClockGroup,
47                                                       "sync_master"))
48                             ->toBool();
49         }
50         if (ControlObject::getControl(ConfigKey(group, "sync_mode"))->get() != SYNC_FOLLOWER) {
51             return false;
52         }
53         if (!ControlObject::getControl(ConfigKey(group, "sync_enabled"))->toBool()) {
54             return false;
55         }
56         if (ControlObject::getControl(ConfigKey(group, "sync_master"))->toBool()) {
57             return false;
58         }
59         return true;
60     }
61 
assertSyncOff(const QString & group)62     void assertSyncOff(const QString& group) {
63         if (group == m_sInternalClockGroup) {
64             ASSERT_EQ(0,
65                     ControlObject::getControl(
66                             ConfigKey(m_sInternalClockGroup, "sync_master"))
67                             ->get());
68         } else {
69             ASSERT_EQ(SYNC_NONE,
70                     ControlObject::getControl(ConfigKey(group, "sync_mode"))
71                             ->get());
72             ASSERT_EQ(0,
73                     ControlObject::getControl(ConfigKey(group, "sync_enabled"))
74                             ->get());
75             ASSERT_EQ(0,
76                     ControlObject::getControl(ConfigKey(group, "sync_master"))
77                             ->get());
78         }
79     }
80 
assertNoMaster()81     void assertNoMaster() {
82         ASSERT_EQ(NULL, m_pEngineSync->getMaster());
83         ASSERT_EQ(NULL, m_pEngineSync->getMasterSyncable());
84     }
85 
86   private:
isMaster(const QString & group,bool explicitMaster)87     bool isMaster(const QString& group, bool explicitMaster) {
88         if (group == m_sInternalClockGroup) {
89             if (!ControlObject::getControl(ConfigKey(m_sInternalClockGroup,
90                                                    "sync_master"))
91                             ->toBool()) {
92                 return false;
93             }
94             if (m_pEngineSync->getMaster()) {
95                 return false;
96             }
97             if (m_sInternalClockGroup != getMasterGroup()) {
98                 return false;
99             }
100             // Internal Clock doesn't have explicit mode
101             if (explicitMaster) {
102                 qWarning() << "test error, internal clock can never be explicit master";
103                 return false;
104             }
105             return true;
106         }
107         if (group == m_sGroup1) {
108             if (m_pEngineSync->getMaster() != m_pChannel1) {
109                 return false;
110             }
111         } else if (group == m_sGroup2) {
112             if (m_pEngineSync->getMaster() != m_pChannel2) {
113                 return false;
114             }
115         }
116         if (getMasterGroup() != group) {
117             return false;
118         }
119 
120         if (explicitMaster) {
121             if (ControlObject::getControl(ConfigKey(group, "sync_mode"))
122                             ->get() != SYNC_MASTER_EXPLICIT) {
123                 return false;
124             }
125         } else {
126             if (ControlObject::getControl(ConfigKey(group, "sync_mode"))
127                             ->get() != SYNC_MASTER_SOFT) {
128                 return false;
129             }
130         }
131         if (!ControlObject::getControl(ConfigKey(group, "sync_enabled"))->toBool()) {
132             return false;
133         }
134         if (!ControlObject::getControl(ConfigKey(group, "sync_master"))->toBool()) {
135             return false;
136         }
137         return true;
138     }
139 };
140 
TEST_F(EngineSyncTest,ControlObjectsExist)141 TEST_F(EngineSyncTest, ControlObjectsExist) {
142     // This isn't exhaustive, but certain COs have a habit of not being set up properly.
143     ASSERT_TRUE(ControlObject::getControl(ConfigKey(m_sGroup1, "file_bpm")) !=
144             NULL);
145 }
146 
TEST_F(EngineSyncTest,SetMasterSuccess)147 TEST_F(EngineSyncTest, SetMasterSuccess) {
148     // If we set the first channel to master, EngineSync should get that message.
149 
150     auto pButtonMasterSync1 =
151             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
152     pButtonMasterSync1->slotSet(SYNC_MASTER_EXPLICIT);
153     ProcessBuffer();
154 
155     // No tracks are playing and we have no beats, SYNC_MASTER_EXPLICIT state is in stand-by
156     EXPECT_DOUBLE_EQ(
157             0.0, ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
158     // The master sync should now be channel 1.
159     ASSERT_TRUE(isExplicitMaster(m_sGroup1));
160 
161     auto pButtonMasterSync2 =
162             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
163     pButtonMasterSync2->set(SYNC_FOLLOWER);
164     ProcessBuffer();
165 
166     ASSERT_TRUE(isFollower(m_sGroup2));
167 
168     // Now set channel 2 to be master.
169     pButtonMasterSync2->set(SYNC_MASTER_EXPLICIT);
170     ProcessBuffer();
171 
172     // Now channel 2 should be master, and channel 1 should be a follower.
173     ASSERT_TRUE(isExplicitMaster(m_sGroup2));
174     ASSERT_TRUE(isFollower(m_sGroup1));
175 
176     // Now back again.
177     pButtonMasterSync1->set(SYNC_MASTER_EXPLICIT);
178     ProcessBuffer();
179 
180     // Now channel 1 should be master, and channel 2 should be a follower.
181     ASSERT_TRUE(isExplicitMaster(m_sGroup1));
182     ASSERT_TRUE(isFollower(m_sGroup2));
183 
184     // Now set channel 1 to follower, no all are followers, waiting for a tempo to adopt.
185     pButtonMasterSync1->slotSet(SYNC_FOLLOWER);
186     ProcessBuffer();
187 
188     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
189     ASSERT_TRUE(isFollower(m_sGroup1));
190     ASSERT_TRUE(isFollower(m_sGroup2));
191 }
192 
TEST_F(EngineSyncTest,ExplicitMasterPersists)193 TEST_F(EngineSyncTest, ExplicitMasterPersists) {
194     // If we set an explicit master, enabling sync or pressing play on other decks
195     // doesn't cause the master to move around.
196     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 120, 0.0);
197     m_pTrack1->trySetBeats(pBeats1);
198     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 124, 0.0);
199     m_pTrack2->trySetBeats(pBeats2);
200 
201     auto pButtonMasterSync1 =
202             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
203     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
204     pButtonMasterSync1->slotSet(SYNC_MASTER_EXPLICIT);
205     ProcessBuffer();
206     // The master sync should now be channel 1.
207     ASSERT_TRUE(isExplicitMaster(m_sGroup1));
208 
209     auto pButtonMasterSync2 =
210             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
211     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
212     pButtonMasterSync2->set(1.0);
213     ProcessBuffer();
214     ASSERT_TRUE(isExplicitMaster(m_sGroup1));
215     ASSERT_TRUE(isFollower(m_sGroup2));
216 
217     // Stop deck 2, and restart it, no change.
218     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(0.0);
219     ProcessBuffer();
220     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
221     ProcessBuffer();
222     ASSERT_TRUE(isExplicitMaster(m_sGroup1));
223     ASSERT_TRUE(isFollower(m_sGroup2));
224 }
225 
TEST_F(EngineSyncTest,SetMasterWhilePlaying)226 TEST_F(EngineSyncTest, SetMasterWhilePlaying) {
227     // Make sure we don't get two master lights if we change masters while playing.
228     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 120, 0.0);
229     m_pTrack1->trySetBeats(pBeats1);
230     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 124, 0.0);
231     m_pTrack2->trySetBeats(pBeats2);
232     mixxx::BeatsPointer pBeats3 = BeatFactory::makeBeatGrid(m_pTrack3->getSampleRate(), 128, 0.0);
233     m_pTrack3->trySetBeats(pBeats3);
234 
235     auto pButtonMasterSync1 =
236             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
237     pButtonMasterSync1->set(SYNC_MASTER_EXPLICIT);
238     auto pButtonMasterSync2 =
239             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
240     pButtonMasterSync2->slotSet(SYNC_FOLLOWER);
241     auto pButtonMasterSync3 =
242             std::make_unique<ControlProxy>(m_sGroup3, "sync_mode");
243     pButtonMasterSync3->slotSet(SYNC_FOLLOWER);
244 
245     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
246     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
247     ControlObject::getControl(ConfigKey(m_sGroup3, "play"))->set(1.0);
248 
249     ProcessBuffer();
250 
251     pButtonMasterSync3->slotSet(SYNC_MASTER_EXPLICIT);
252 
253     ProcessBuffer();
254 
255     ASSERT_TRUE(isFollower(m_sGroup1));
256     ASSERT_TRUE(isFollower(m_sGroup2));
257     ASSERT_TRUE(isExplicitMaster(m_sGroup3));
258 }
259 
TEST_F(EngineSyncTest,SetEnabledBecomesMaster)260 TEST_F(EngineSyncTest, SetEnabledBecomesMaster) {
261     // If we set the first channel with a valid tempo to follower, it should be master.
262     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 80, 0.0);
263     m_pTrack1->trySetBeats(pBeats1);
264     auto pButtonMasterSync1 =
265             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
266     pButtonMasterSync1->slotSet(SYNC_FOLLOWER);
267     ProcessBuffer();
268 
269     ASSERT_TRUE(isSoftMaster(m_sGroup1));
270     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
271 }
272 
TEST_F(EngineSyncTest,DisableInternalMasterWhilePlaying)273 TEST_F(EngineSyncTest, DisableInternalMasterWhilePlaying) {
274     auto pButtonMasterSync = std::make_unique<ControlProxy>(
275             m_sInternalClockGroup, "sync_master");
276     pButtonMasterSync->slotSet(1.0);
277     auto pButtonSyncMode1 =
278             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
279     pButtonSyncMode1->slotSet(SYNC_FOLLOWER);
280     auto pButtonSyncMode2 =
281             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
282     pButtonSyncMode2->slotSet(SYNC_FOLLOWER);
283     ProcessBuffer();
284 
285     // The master sync should now be Internal.
286     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
287 
288     // Make sure both decks are playing.
289     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 80, 0.0);
290     m_pTrack1->trySetBeats(pBeats1);
291     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
292     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 80, 0.0);
293     m_pTrack2->trySetBeats(pBeats2);
294     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
295     ProcessBuffer();
296 
297     // Now unset Internal master.
298     pButtonMasterSync->slotSet(0.0);
299     ProcessBuffer();
300 
301     // This is not allowed, Internal should still be master.
302     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
303     ASSERT_EQ(1, pButtonMasterSync->get());
304 }
305 
TEST_F(EngineSyncTest,DisableSyncOnMaster)306 TEST_F(EngineSyncTest, DisableSyncOnMaster) {
307     // Channel 1 follower, channel 2 master.
308     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
309     m_pTrack1->trySetBeats(pBeats1);
310     auto pButtonSyncMode1 =
311             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
312     pButtonSyncMode1->slotSet(SYNC_FOLLOWER);
313 
314     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 130, 0.0);
315     m_pTrack2->trySetBeats(pBeats2);
316     auto pButtonSyncMaster2 =
317             std::make_unique<ControlProxy>(m_sGroup2, "sync_master");
318     pButtonSyncMaster2->slotSet(1.0);
319 
320     // TODO(owilliams): explicit master is disabled, so regular master sync is
321     // enabled instead.
322     ASSERT_TRUE(isFollower(m_sGroup1));
323     ASSERT_TRUE(isFollower(m_sGroup2));
324     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
325 
326     // Unset enabled on channel2, it should work.
327     auto pButtonSyncEnabled2 =
328             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
329     pButtonSyncEnabled2->slotSet(0.0);
330 
331     ASSERT_TRUE(isSoftMaster(m_sGroup1));
332     ASSERT_EQ(0, ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->get());
333     ASSERT_EQ(0, ControlObject::getControl(ConfigKey(m_sGroup2, "sync_master"))->get());
334 }
335 
TEST_F(EngineSyncTest,InternalMasterSetFollowerSliderMoves)336 TEST_F(EngineSyncTest, InternalMasterSetFollowerSliderMoves) {
337     // If internal is master, and we turn on a follower, the slider should move.
338     auto pButtonMasterSyncInternal = std::make_unique<ControlProxy>(
339             m_sInternalClockGroup, "sync_master");
340     auto pMasterSyncSlider =
341             std::make_unique<ControlProxy>(m_sInternalClockGroup, "bpm");
342 
343     pMasterSyncSlider->set(100.0);
344     pButtonMasterSyncInternal->slotSet(1);
345 
346     // Set the file bpm of channel 1 to 80 bpm.
347     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 80, 0.0);
348     m_pTrack1->trySetBeats(pBeats1);
349 
350     auto pButtonMasterSync1 =
351             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
352     pButtonMasterSync1->slotSet(SYNC_FOLLOWER);
353     ProcessBuffer();
354 
355     EXPECT_DOUBLE_EQ(getRateSliderValue(1.25),
356             ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))->get());
357     EXPECT_DOUBLE_EQ(100.0,
358             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
359 }
360 
TEST_F(EngineSyncTest,AnySyncDeckSliderStays)361 TEST_F(EngineSyncTest, AnySyncDeckSliderStays) {
362     // If there exists a sync deck, even if it's not playing, don't change the
363     // master BPM if a new deck enables sync.
364 
365     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 80, 0.0);
366     m_pTrack1->trySetBeats(pBeats1);
367     auto pButtonSyncEnabled1 =
368             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
369     pButtonSyncEnabled1->set(1.0);
370 
371     // After setting up the first deck, the internal BPM should be 80.
372     EXPECT_DOUBLE_EQ(80.0,
373             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
374                     ->get());
375 
376     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 100, 0.0);
377     m_pTrack2->trySetBeats(pBeats2);
378     auto pButtonSyncEnabled2 =
379             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
380     pButtonSyncEnabled2->set(1.0);
381 
382     // After the second one, though, the internal BPM should still be 80.
383     EXPECT_DOUBLE_EQ(80.0,
384             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
385                     ->get());
386 }
387 
TEST_F(EngineSyncTest,InternalClockFollowsFirstPlayingDeck)388 TEST_F(EngineSyncTest, InternalClockFollowsFirstPlayingDeck) {
389     // Same as above, except we use the midi lights to change state.
390     auto pButtonMasterSync1 =
391             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
392     auto pButtonMasterSync2 =
393             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
394     auto pButtonSyncEnabled1 =
395             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
396     auto pButtonSyncEnabled2 =
397             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
398 
399     // Set up decks so they can be playing, and start deck 1.
400     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 100, 0.0);
401     m_pTrack1->trySetBeats(pBeats1);
402     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
403     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
404     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 130, 0.0);
405     m_pTrack2->trySetBeats(pBeats2);
406     ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0));
407     ControlObject::set(ConfigKey(m_sGroup2, "play"), 0.0);
408     ProcessBuffer();
409 
410     // Set channel 1 to be enabled
411     pButtonSyncEnabled1->set(1.0);
412     ProcessBuffer();
413 
414     // The master sync should now be deck 1.
415     ASSERT_TRUE(isSoftMaster(m_sGroup1));
416     EXPECT_DOUBLE_EQ(100.0,
417             ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
418 
419     // Set channel 2 to be enabled.
420     pButtonSyncEnabled2->set(1);
421     ProcessBuffer();
422 
423     // channel 1 still master while 2 is not playing
424     ASSERT_TRUE(isSoftMaster(m_sGroup1));
425     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
426     ASSERT_TRUE(isFollower(m_sGroup2));
427 
428     // The rate should not have changed -- deck 1 still matches deck 2.
429     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
430             ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))->get());
431 
432     // Reset channel 2 rate, set channel 2 to play, and process a buffer.
433     ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0));
434     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
435     ProcessBuffer();
436     // Now internal clock is master
437     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
438     ASSERT_TRUE(isFollower(m_sGroup1));
439     ASSERT_TRUE(isFollower(m_sGroup2));
440 
441     // Now disable sync on channel 1.
442     pButtonSyncEnabled1->set(0);
443     ProcessBuffer();
444 
445     // Master flips to deck 2
446     ASSERT_TRUE(isSoftMaster(m_sGroup2));
447     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
448 
449     // Rate should now match channel 2.
450     EXPECT_DOUBLE_EQ(
451             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
452 }
453 
TEST_F(EngineSyncTest,SetExplicitMasterByLights)454 TEST_F(EngineSyncTest, SetExplicitMasterByLights) {
455     // Same as above, except we use the midi lights to change state.
456     auto pButtonMasterSync1 =
457             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
458     auto pButtonMasterSync2 =
459             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
460     auto pButtonSyncEnabled1 =
461             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
462     auto pButtonSyncEnabled2 =
463             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
464     auto pButtonSyncMaster1 =
465             std::make_unique<ControlProxy>(m_sGroup1, "sync_master");
466     auto pButtonSyncMaster2 =
467             std::make_unique<ControlProxy>(m_sGroup2, "sync_master");
468 
469     // Set the file bpm of channel 1 to 160bpm.
470     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
471     m_pTrack1->trySetBeats(pBeats1);
472     // Set the file bpm of channel 2 to 150bpm.
473     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 150, 0.0);
474     m_pTrack2->trySetBeats(pBeats2);
475 
476     // Set channel 1 to be explicit master.
477     pButtonSyncMaster1->slotSet(1.0);
478     ProcessBuffer();
479 
480     // The master sync should now be channel 1.
481     // TODO(owilliams): Because explicit master is broken, currently this deck will just
482     // be a soft master.  This is intended but will be fixed.
483     ASSERT_TRUE(isSoftMaster(m_sGroup1));
484 
485     // Set channel 2 to be follower.
486     pButtonSyncEnabled2->slotSet(1);
487 
488     ASSERT_TRUE(isFollower(m_sGroup2));
489 
490     // Now set channel 2 to be master.
491     pButtonSyncMaster2->slotSet(1);
492 
493     // Now channel 2 should be master, and channel 1 should be a follower.
494     // TODO(owilliams): Because explicit master is broken, these will both be followers.
495     // This will be fixed
496     ASSERT_TRUE(isFollower(m_sGroup1));
497     ASSERT_TRUE(isFollower(m_sGroup2));
498     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
499 
500     // Now back again.
501     pButtonSyncMaster1->slotSet(1);
502 
503     // Now channel 1 should be master, and channel 2 should be a follower.
504     // TODO(owilliams): See above
505     ASSERT_TRUE(isFollower(m_sGroup1));
506     ASSERT_TRUE(isFollower(m_sGroup2));
507     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
508 
509     // Now set channel 1 to not-master, all will become follower.
510     // handing over master to the internal clock
511     pButtonSyncMaster1->slotSet(0);
512 
513     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
514     ASSERT_TRUE(isFollower(m_sGroup1));
515     ASSERT_TRUE(isFollower(m_sGroup2));
516 }
517 
TEST_F(EngineSyncTest,SetExplicitMasterByLightsNoTracks)518 TEST_F(EngineSyncTest, SetExplicitMasterByLightsNoTracks) {
519     // Same as above, except we use the midi lights to change state.
520     auto pButtonSyncEnabled2 =
521             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
522     auto pButtonSyncMaster1 =
523             std::make_unique<ControlProxy>(m_sGroup1, "sync_master");
524 
525     // Now back again.
526     pButtonSyncMaster1->slotSet(1);
527 
528     // Set channel 2 to be follower.
529     pButtonSyncEnabled2->slotSet(1);
530 
531     // Now channel 1 should be master, and channel 2 should be a follower.
532     // Because there are no bpms anywhere, internal clock is also follower.
533     // TODO(owilliams): Because explicit master is broken, both tracks are followers.
534     ASSERT_TRUE(isFollower(m_sGroup1));
535     ASSERT_TRUE(isFollower(m_sGroup2));
536     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
537 
538     // Now set channel 1 to not-master, all will be follower waiting for a valid bpm.
539     pButtonSyncMaster1->slotSet(0);
540 
541     ASSERT_TRUE(isFollower(m_sGroup1));
542     ASSERT_TRUE(isFollower(m_sGroup2));
543     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
544 }
545 
TEST_F(EngineSyncTest,RateChangeTest)546 TEST_F(EngineSyncTest, RateChangeTest) {
547     auto pButtonMasterSync1 =
548             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
549     pButtonMasterSync1->set(SYNC_MASTER_EXPLICIT);
550     auto pButtonMasterSync2 =
551             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
552     pButtonMasterSync2->set(SYNC_FOLLOWER);
553     ProcessBuffer();
554 
555     // Set the file bpm of channel 1 to 160bpm.
556     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
557     m_pTrack1->trySetBeats(pBeats1);
558     EXPECT_DOUBLE_EQ(
559             160.0, ControlObject::get(ConfigKey(m_sGroup1, "file_bpm")));
560     ProcessBuffer();
561     EXPECT_DOUBLE_EQ(
562             160.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
563 
564     // Set the rate of channel 1 to 1.2.
565     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.2));
566     EXPECT_DOUBLE_EQ(getRateSliderValue(1.2),
567             ControlObject::get(ConfigKey(m_sGroup1, "rate")));
568     EXPECT_DOUBLE_EQ(192.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
569 
570     // Internal master should also be 192.
571     EXPECT_DOUBLE_EQ(
572             192.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
573 
574     // Set the file bpm of channel 2 to 120bpm.
575     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
576     m_pTrack2->trySetBeats(pBeats2);
577     EXPECT_DOUBLE_EQ(
578             120.0, ControlObject::get(ConfigKey(m_sGroup2, "file_bpm")));
579 
580     // rate slider for channel 2 should now be 1.6 = 160 * 1.2 / 120.
581     EXPECT_DOUBLE_EQ(getRateSliderValue(1.6),
582             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
583     EXPECT_DOUBLE_EQ(192.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
584 }
585 
TEST_F(EngineSyncTest,RateChangeTestWeirdOrder)586 TEST_F(EngineSyncTest, RateChangeTestWeirdOrder) {
587     // This is like the test above, but the user loads the track after the slider has been tweaked.
588     auto pButtonMasterSync1 =
589             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
590     pButtonMasterSync1->slotSet(SYNC_MASTER_EXPLICIT);
591     auto pButtonMasterSync2 =
592             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
593     pButtonMasterSync2->slotSet(SYNC_FOLLOWER);
594     ProcessBuffer();
595 
596     // Set the file bpm of channel 1 to 160bpm.
597     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
598     m_pTrack1->trySetBeats(pBeats1);
599     EXPECT_DOUBLE_EQ(
600             160.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
601 
602     // Set the file bpm of channel 2 to 120bpm.
603     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
604     m_pTrack2->trySetBeats(pBeats2);
605 
606     // Set the rate slider of channel 1 to 1.2.
607     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.2));
608 
609     // Rate slider for channel 2 should now be 1.6 = (160 * 1.2 / 120) - 1.0.
610     EXPECT_DOUBLE_EQ(getRateSliderValue(1.6),
611             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
612     EXPECT_DOUBLE_EQ(192.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
613 
614     // Internal Master BPM should read the same.
615     EXPECT_DOUBLE_EQ(
616             192.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
617 }
618 
TEST_F(EngineSyncTest,RateChangeTestOrder3)619 TEST_F(EngineSyncTest, RateChangeTestOrder3) {
620     // Set the file bpm of channel 1 to 160bpm.
621     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
622     m_pTrack1->trySetBeats(pBeats1);
623     EXPECT_DOUBLE_EQ(
624             160.0, ControlObject::get(ConfigKey(m_sGroup1, "file_bpm")));
625 
626     // Set the file bpm of channel 2 to 120bpm.
627     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
628     m_pTrack2->trySetBeats(pBeats2);
629     EXPECT_DOUBLE_EQ(
630             120.0, ControlObject::get(ConfigKey(m_sGroup2, "file_bpm")));
631 
632     // Turn on Master and Follower.
633     auto pButtonMasterSync1 =
634             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
635     pButtonMasterSync1->set(SYNC_MASTER_EXPLICIT);
636     ProcessBuffer();
637 
638     ASSERT_TRUE(isExplicitMaster(m_sGroup1));
639 
640     auto pButtonMasterSync2 =
641             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
642     pButtonMasterSync2->set(SYNC_FOLLOWER);
643     ProcessBuffer();
644 
645     // Follower should immediately set its slider.
646     EXPECT_NEAR(getRateSliderValue(1.3333333333),
647             ControlObject::get(ConfigKey(m_sGroup2, "rate")),
648             kMaxFloatingPointErrorLowPrecision);
649     EXPECT_DOUBLE_EQ(160.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
650     EXPECT_DOUBLE_EQ(
651             160.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
652 }
653 
TEST_F(EngineSyncTest,FollowerRateChange)654 TEST_F(EngineSyncTest, FollowerRateChange) {
655     // Confirm that followers can change master sync rate as well.
656     auto pButtonMasterSync1 =
657             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
658     pButtonMasterSync1->set(SYNC_MASTER_EXPLICIT);
659     auto pButtonMasterSync2 =
660             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
661     pButtonMasterSync2->set(SYNC_FOLLOWER);
662     ProcessBuffer();
663 
664     // Set the file bpm of channel 1 to 160bpm.
665     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
666     m_pTrack1->trySetBeats(pBeats1);
667 
668     // Set the file bpm of channel 2 to 120bpm.
669     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
670     m_pTrack2->trySetBeats(pBeats2);
671 
672     // Set the rate slider of channel 1 to 1.2.
673     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.2));
674 
675     // Rate slider for channel 2 should now be 1.6 = (160 * 1.2 / 120).
676     EXPECT_DOUBLE_EQ(getRateSliderValue(1.6),
677             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
678     EXPECT_DOUBLE_EQ(192.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
679 
680     // Try to twiddle the rate slider on channel 2.
681     auto pSlider2 = std::make_unique<ControlProxy>(m_sGroup2, "rate");
682     pSlider2->set(getRateSliderValue(0.8));
683     ProcessBuffer();
684 
685     // Rates should still be changed even though it's a follower.
686     EXPECT_DOUBLE_EQ(getRateSliderValue(0.8),
687             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
688     EXPECT_DOUBLE_EQ(96.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
689     EXPECT_DOUBLE_EQ(getRateSliderValue(0.6),
690             ControlObject::get(ConfigKey(m_sGroup1, "rate")));
691     EXPECT_DOUBLE_EQ(96.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
692 }
693 
TEST_F(EngineSyncTest,InternalRateChangeTest)694 TEST_F(EngineSyncTest, InternalRateChangeTest) {
695     auto pButtonMasterSyncInternal = std::make_unique<ControlProxy>(
696             m_sInternalClockGroup, "sync_master");
697     pButtonMasterSyncInternal->set(SYNC_MASTER_SOFT);
698     auto pButtonMasterSync1 =
699             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
700     pButtonMasterSync1->set(SYNC_FOLLOWER);
701     auto pButtonMasterSync2 =
702             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
703     pButtonMasterSync2->set(SYNC_FOLLOWER);
704     ProcessBuffer();
705 
706     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
707     ASSERT_TRUE(isFollower(m_sGroup1));
708     ASSERT_TRUE(isFollower(m_sGroup2));
709 
710     // Set the file bpm of channel 1 to 160bpm.
711     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
712     m_pTrack1->trySetBeats(pBeats1);
713     EXPECT_DOUBLE_EQ(160.0,
714             ControlObject::getControl(ConfigKey(m_sGroup1, "file_bpm"))->get());
715 
716     // Set the file bpm of channel 2 to 120bpm.
717     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
718     m_pTrack2->trySetBeats(pBeats2);
719     EXPECT_DOUBLE_EQ(120.0,
720             ControlObject::getControl(ConfigKey(m_sGroup2, "file_bpm"))->get());
721 
722     // Set the internal rate to 150.
723     auto pMasterSyncSlider =
724             std::make_unique<ControlProxy>(m_sInternalClockGroup, "bpm");
725     pMasterSyncSlider->set(150.0);
726     EXPECT_DOUBLE_EQ(150.0,
727             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
728                     ->get());
729     // Set decks playing, and process a buffer to update all the COs.
730     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
731     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
732 
733     ProcessBuffer();
734 
735     // Rate sliders for channels 1 and 2 should change appropriately.
736     EXPECT_DOUBLE_EQ(getRateSliderValue(0.9375),
737             ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))->get());
738     EXPECT_DOUBLE_EQ(150.0,
739             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
740     EXPECT_DOUBLE_EQ(getRateSliderValue(1.25),
741             ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get());
742     EXPECT_DOUBLE_EQ(150.0,
743             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
744 
745     // Set the internal rate to 140.
746     pMasterSyncSlider->set(140.0);
747 
748     // Update COs again.
749     ProcessBuffer();
750 
751     // Rate sliders for channels 1 and 2 should change appropriately.
752     EXPECT_DOUBLE_EQ(getRateSliderValue(.875),
753             ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))->get());
754     EXPECT_DOUBLE_EQ(140.0,
755             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
756     EXPECT_NEAR(getRateSliderValue(1.16666667),
757             ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get(),
758             kMaxFloatingPointErrorLowPrecision);
759     EXPECT_DOUBLE_EQ(140.0,
760             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
761 }
762 
TEST_F(EngineSyncTest,MasterStopSliderCheck)763 TEST_F(EngineSyncTest, MasterStopSliderCheck) {
764     // If the master is playing, and stop is pushed, the sliders should stay the same.
765     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 120, 0.0);
766     m_pTrack1->trySetBeats(pBeats1);
767     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 128, 0.0);
768     m_pTrack2->trySetBeats(pBeats2);
769 
770     auto pButtonMasterSync1 =
771             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
772     pButtonMasterSync1->slotSet(SYNC_MASTER_EXPLICIT);
773     auto pButtonMasterSync2 =
774             std::make_unique<ControlProxy>(m_sGroup2, "sync_mode");
775     pButtonMasterSync2->slotSet(SYNC_FOLLOWER);
776     ProcessBuffer();
777 
778     //ASSERT_TRUE(isExplicitMaster(m_sGroup1));
779     ASSERT_TRUE(isFollower(m_sGroup2));
780 
781     auto pChannel1Play = std::make_unique<ControlProxy>(m_sGroup1, "play");
782     pChannel1Play->set(1.0);
783     auto pChannel2Play = std::make_unique<ControlProxy>(m_sGroup2, "play");
784     pChannel2Play->set(1.0);
785 
786     ProcessBuffer();
787 
788     EXPECT_DOUBLE_EQ(120.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
789     EXPECT_DOUBLE_EQ(getRateSliderValue(0.9375),
790             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
791 
792     pChannel1Play->set(0.0);
793 
794     ProcessBuffer();
795 
796     EXPECT_DOUBLE_EQ(120.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
797     EXPECT_DOUBLE_EQ(getRateSliderValue(0.9375),
798             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
799 }
800 
TEST_F(EngineSyncTest,EnableOneDeckInitsMaster)801 TEST_F(EngineSyncTest, EnableOneDeckInitsMaster) {
802     // If Internal is master, and we turn sync on a playing deck, the playing deck sets the
803     // internal master and the beat distances are now aligned.
804     ControlObject::set(ConfigKey(m_sInternalClockGroup, "bpm"), 124.0);
805     ControlObject::set(ConfigKey(m_sInternalClockGroup, "beat_distance"), 0.5);
806     ProcessBuffer();
807 
808     // Set up the deck to play.
809     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
810     m_pTrack1->trySetBeats(pBeats1);
811     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
812             ->set(getRateSliderValue(1.0));
813     ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->set(0.2);
814     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
815 
816     // Enable Sync.  We have to call requestEnableSync directly
817     // because calling ProcessBuffer() tries to advance the beat_distance values.
818     m_pEngineSync->requestEnableSync(
819             m_pEngineSync->getSyncableForGroup(m_sGroup1), true);
820 
821     // Internal is no longer master because there is exactly one playing deck.
822     ASSERT_TRUE(isSoftMaster(m_sGroup1));
823     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
824 
825     // Internal clock rate and beat distance should match that deck.
826     EXPECT_DOUBLE_EQ(
827             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
828     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
829     EXPECT_DOUBLE_EQ(
830             0.2, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
831     EXPECT_DOUBLE_EQ(0.2,
832             ControlObject::get(
833                     ConfigKey(m_sInternalClockGroup, "beat_distance")));
834 
835     // Enable second deck, bpm and beat distance should still match original setting.
836     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 140, 0.0);
837     m_pTrack2->trySetBeats(pBeats2);
838     ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))
839             ->set(getRateSliderValue(1.0));
840     ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->set(0.2);
841     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
842 
843     m_pEngineSync->requestEnableSync(
844             m_pEngineSync->getSyncableForGroup(m_sGroup2), true);
845     // Now master should be Internal Clock because we have two playing decks.
846     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
847     ASSERT_TRUE(isFollower(m_sGroup1));
848     ASSERT_TRUE(isFollower(m_sGroup2));
849 
850     EXPECT_DOUBLE_EQ(
851             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
852     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
853     EXPECT_DOUBLE_EQ(
854             0.2, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
855     EXPECT_DOUBLE_EQ(0.2,
856             ControlObject::get(
857                     ConfigKey(m_sInternalClockGroup, "beat_distance")));
858 }
859 
TEST_F(EngineSyncTest,EnableOneDeckInitializesMaster)860 TEST_F(EngineSyncTest, EnableOneDeckInitializesMaster) {
861     // Enabling sync on a deck causes it to be master, and sets bpm and clock.
862     // Set the deck to play.
863     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
864     m_pTrack1->trySetBeats(pBeats1);
865     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
866             ->set(getRateSliderValue(1.0));
867     ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->set(0.2);
868     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
869 
870     // Set the deck to follower.
871     // As above, use direct call to avoid advancing beat distance.
872     m_pEngineSync->requestEnableSync(
873             m_pEngineSync->getSyncableForGroup(m_sGroup1), true);
874 
875     // That first deck is now master
876     ASSERT_TRUE(isSoftMaster(m_sGroup1));
877 
878     // Internal clock rate should be set and beat distances reset.
879     EXPECT_DOUBLE_EQ(130.0,
880             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
881                     ->get());
882     EXPECT_DOUBLE_EQ(130.0,
883             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
884     EXPECT_DOUBLE_EQ(0.2,
885             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
886                     ->get());
887     EXPECT_DOUBLE_EQ(0.2,
888             ControlObject::getControl(
889                     ConfigKey(m_sInternalClockGroup, "beat_distance"))
890                     ->get());
891 }
892 
TEST_F(EngineSyncTest,LoadTrackInitializesMaster)893 TEST_F(EngineSyncTest, LoadTrackInitializesMaster) {
894     // First eject the fake tracks that come with the testing framework.
895     m_pChannel1->getEngineBuffer()->slotEjectTrack(1.0);
896     m_pChannel2->getEngineBuffer()->slotEjectTrack(1.0);
897     m_pChannel3->getEngineBuffer()->slotEjectTrack(1.0);
898 
899     auto pButtonSyncEnabled1 =
900             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
901     pButtonSyncEnabled1->slotSet(1.0);
902 
903     // No master because this deck has no track.
904     EXPECT_EQ(NULL, m_pEngineSync->getMaster());
905     EXPECT_DOUBLE_EQ(
906             0.0, ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
907 
908     // The track load trigger a master change.
909     m_pMixerDeck1->loadFakeTrack(false, 140.0);
910     ASSERT_TRUE(isSoftMaster(m_sGroup1));
911     EXPECT_DOUBLE_EQ(140.0,
912             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
913 
914     // But as soon as we play, deck 1 is master
915     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
916     ASSERT_TRUE(isSoftMaster(m_sGroup1));
917     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(0.0);
918 
919     // If sync is on two decks and we load a track in only one of them, it will be
920     // master.
921     m_pChannel1->getEngineBuffer()->slotEjectTrack(1.0);
922     ASSERT_TRUE(isFollower(m_sGroup1));
923     // no relevant tempo available so internal clock is following
924     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
925 
926     auto pButtonSyncEnabled2 =
927             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
928     pButtonSyncEnabled2->slotSet(1.0);
929 
930     m_pMixerDeck1->loadFakeTrack(false, 128.0);
931 
932     // Deck 2 is still empty so Deck 1 becomes master again
933     ASSERT_TRUE(isSoftMaster(m_sGroup1));
934     EXPECT_DOUBLE_EQ(128.0,
935             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
936 
937     // If sync is on two decks and one deck is loaded but not playing, we should
938     // initialize to that deck with internal clock master.
939     m_pMixerDeck2->loadFakeTrack(false, 110.0);
940 
941     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
942     EXPECT_DOUBLE_EQ(128.0,
943             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
944                     ->get());
945     EXPECT_DOUBLE_EQ(128.0,
946             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
947     EXPECT_DOUBLE_EQ(128.0,
948             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
949 }
950 
TEST_F(EngineSyncTest,LoadTrackResetTempoOption)951 TEST_F(EngineSyncTest, LoadTrackResetTempoOption) {
952     // Make sure playing decks with master sync enabled do not change tempo when
953     // the "Reset Speed/Tempo" preference is set and a track is loaded to another
954     // deck with master sync enabled.
955     m_pConfig->set(ConfigKey("[Controls]", "SpeedAutoReset"),
956             ConfigValue(BaseTrackPlayer::RESET_SPEED));
957 
958     // Enable sync on two stopped decks
959     auto pButtonSyncEnabled1 =
960             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
961     pButtonSyncEnabled1->set(1.0);
962 
963     auto pButtonSyncEnabled2 =
964             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
965     pButtonSyncEnabled2->set(1.0);
966 
967     // If sync is on and we load a track, that should initialize master.
968     TrackPointer track1 = m_pMixerDeck1->loadFakeTrack(false, 140.0);
969 
970     EXPECT_DOUBLE_EQ(
971             140.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
972     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
973 
974     // If sync is on two decks and we load a track while one is playing,
975     // that should not change the playing deck.
976     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
977 
978     TrackPointer track2 = m_pMixerDeck2->loadFakeTrack(false, 128.0);
979 
980     EXPECT_DOUBLE_EQ(
981             140.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
982     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
983     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
984 
985     // Repeat with RESET_PITCH_AND_SPEED
986     m_pConfig->set(ConfigKey("[Controls]", "SpeedAutoReset"),
987             ConfigValue(BaseTrackPlayer::RESET_PITCH_AND_SPEED));
988     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
989     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
990     track1 = m_pMixerDeck1->loadFakeTrack(false, 140.0);
991     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
992     track2 = m_pMixerDeck2->loadFakeTrack(false, 128.0);
993     EXPECT_DOUBLE_EQ(
994             140.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
995     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
996     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
997 
998     // Repeat with RESET_NONE
999     m_pConfig->set(ConfigKey("[Controls]", "SpeedAutoReset"),
1000             ConfigValue(BaseTrackPlayer::RESET_NONE));
1001     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
1002     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1003     ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0));
1004     track1 = m_pMixerDeck1->loadFakeTrack(false, 140.0);
1005     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
1006     track2 = m_pMixerDeck2->loadFakeTrack(false, 128.0);
1007     EXPECT_DOUBLE_EQ(
1008             128.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1009     EXPECT_DOUBLE_EQ(128.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
1010     EXPECT_DOUBLE_EQ(128.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
1011 
1012     // Load two tracks with sync off and RESET_SPEED
1013     m_pConfig->set(ConfigKey("[Controls]", "SpeedAutoReset"),
1014             ConfigValue(BaseTrackPlayer::RESET_SPEED));
1015     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(0.0);
1016     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1017             ->set(getRateSliderValue(1.5));
1018     ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))
1019             ->set(getRateSliderValue(1.5));
1020     pButtonSyncEnabled1->set(0.0);
1021     pButtonSyncEnabled2->set(0.0);
1022     track1 = m_pMixerDeck1->loadFakeTrack(false, 140.0);
1023     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
1024     track2 = m_pMixerDeck2->loadFakeTrack(false, 128.0);
1025     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
1026     EXPECT_DOUBLE_EQ(128.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
1027 
1028     // Load two tracks with sync off and RESET_PITCH_AND_SPEED
1029     m_pConfig->set(ConfigKey("[Controls]", "SpeedAutoReset"),
1030             ConfigValue(BaseTrackPlayer::RESET_PITCH_AND_SPEED));
1031     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(0.0);
1032     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1033             ->set(getRateSliderValue(1.5));
1034     ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))
1035             ->set(getRateSliderValue(1.5));
1036     pButtonSyncEnabled1->slotSet(0.0);
1037     pButtonSyncEnabled2->slotSet(0.0);
1038     track1 = m_pMixerDeck1->loadFakeTrack(false, 140.0);
1039     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1040     track2 = m_pMixerDeck2->loadFakeTrack(false, 128.0);
1041     EXPECT_DOUBLE_EQ(140.0,
1042             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1043     EXPECT_DOUBLE_EQ(128.0,
1044             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1045 }
1046 
TEST_F(EngineSyncTest,EnableOneDeckSliderUpdates)1047 TEST_F(EngineSyncTest, EnableOneDeckSliderUpdates) {
1048     // If we enable a deck to be master, the internal slider should immediately update.
1049     auto pButtonSyncEnabled1 =
1050             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1051 
1052     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
1053     m_pTrack1->trySetBeats(pBeats1);
1054     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1055             ->set(getRateSliderValue(1.0));
1056 
1057     // Set the deck to sync enabled.
1058     pButtonSyncEnabled1->slotSet(1.0);
1059     ProcessBuffer();
1060 
1061     // Group 1 should now be master (only one sync deck).
1062     ASSERT_TRUE(isSoftMaster(m_sGroup1));
1063 
1064     // Internal clock rate should be set.
1065     EXPECT_DOUBLE_EQ(130.0,
1066             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
1067                     ->get());
1068 }
1069 
TEST_F(EngineSyncTest,SyncToNonSyncDeck)1070 TEST_F(EngineSyncTest, SyncToNonSyncDeck) {
1071     // If deck 1 is playing, and deck 2 presses sync, deck 2 should sync to deck 1 even if
1072     // deck 1 is not a sync deck.
1073 
1074     auto pButtonSyncEnabled1 =
1075             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1076     auto pButtonSyncEnabled2 =
1077             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1078 
1079     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
1080     m_pTrack1->trySetBeats(pBeats1);
1081     ProcessBuffer();
1082     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1083     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 100, 0.0);
1084     m_pTrack2->trySetBeats(pBeats2);
1085     ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))
1086             ->set(getRateSliderValue(1.0));
1087 
1088     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
1089     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
1090     ProcessBuffer();
1091 
1092     pButtonSyncEnabled2->set(1.0);
1093     pButtonSyncEnabled2->set(0.0);
1094     ProcessBuffer();
1095 
1096     // There should be no master, and deck2 should match rate of deck1.  Sync slider should be
1097     // updated with the value, however.
1098     assertNoMaster();
1099     EXPECT_DOUBLE_EQ(
1100             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1101     assertSyncOff(m_sGroup2);
1102     EXPECT_DOUBLE_EQ(getRateSliderValue(1.3),
1103             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
1104 
1105     // Reset the pitch of deck 2.
1106     ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0));
1107 
1108     // The same should work in reverse.
1109     pButtonSyncEnabled1->set(1.0);
1110     pButtonSyncEnabled1->set(0.0);
1111     ProcessBuffer();
1112 
1113     // There should be no master, and deck2 should match rate of deck1.
1114     EXPECT_EQ(0,
1115             ControlObject::get(
1116                     ConfigKey(m_sInternalClockGroup, "sync_master")));
1117     EXPECT_DOUBLE_EQ(
1118             100.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1119     EXPECT_EQ(NULL, m_pEngineSync->getMaster());
1120     EXPECT_EQ(NULL, m_pEngineSync->getMasterSyncable());
1121     EXPECT_EQ(SYNC_NONE, ControlObject::get(ConfigKey(m_sGroup1, "sync_mode")));
1122     EXPECT_EQ(0, ControlObject::get(ConfigKey(m_sGroup1, "sync_enabled")));
1123     EXPECT_EQ(0, ControlObject::get(ConfigKey(m_sGroup1, "sync_master")));
1124     EXPECT_DOUBLE_EQ(getRateSliderValue(100.0 / 130.0),
1125             ControlObject::get(ConfigKey(m_sGroup1, "rate")));
1126 
1127     // Reset again.
1128     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1129 
1130     // If deck 1 is not playing, however, deck 2 should stay at the same rate.
1131     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
1132 
1133     // The same should work in reverse.
1134     pButtonSyncEnabled1->set(1.0);
1135     pButtonSyncEnabled1->set(0.0);
1136     ProcessBuffer();
1137 
1138     // There should be no master, and deck2 should match rate of deck1.
1139     EXPECT_EQ(0,
1140             ControlObject::get(
1141                     ConfigKey(m_sInternalClockGroup, "sync_master")));
1142     EXPECT_DOUBLE_EQ(
1143             100.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1144     EXPECT_EQ(NULL, m_pEngineSync->getMaster());
1145     EXPECT_EQ(NULL, m_pEngineSync->getMasterSyncable());
1146     EXPECT_EQ(SYNC_NONE, ControlObject::get(ConfigKey(m_sGroup2, "sync_mode")));
1147     EXPECT_EQ(0, ControlObject::get(ConfigKey(m_sGroup2, "sync_enabled")));
1148     EXPECT_EQ(0, ControlObject::get(ConfigKey(m_sGroup2, "sync_master")));
1149     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
1150             ControlObject::get(ConfigKey(m_sGroup2, "rate")));
1151 }
1152 
TEST_F(EngineSyncTest,MomentarySyncDependsOnPlayingStates)1153 TEST_F(EngineSyncTest, MomentarySyncDependsOnPlayingStates) {
1154     // Like it says -- if the current deck is playing, and the target deck is
1155     // playing, they should sync even if there's no sync mode enabled.
1156 
1157     auto pButtonSyncEnabled1 =
1158             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1159     auto pButtonSyncEnabled2 =
1160             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1161 
1162     // Set up decks so they can be playing, and start deck 1.
1163     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 100, 0.0);
1164     m_pTrack1->trySetBeats(pBeats1);
1165     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1166     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
1167     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 130, 0.0);
1168     m_pTrack2->trySetBeats(pBeats2);
1169     ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0));
1170     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
1171     ProcessBuffer();
1172     ProcessBuffer();
1173 
1174     // Set channel 1 to be enabled momentarily.
1175     pButtonSyncEnabled1->set(1.0);
1176     pButtonSyncEnabled1->set(0.0);
1177     ProcessBuffer();
1178 
1179     // The master sync should still be off and the speed should match deck 2.
1180     assertNoMaster();
1181     assertSyncOff(m_sGroup1);
1182     assertSyncOff(m_sGroup2);
1183     EXPECT_DOUBLE_EQ(
1184             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1185 
1186     // Also works if deck 1 is not playing.
1187     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1188     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
1189     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
1190     ProcessBuffer();
1191     pButtonSyncEnabled1->set(1.0);
1192     pButtonSyncEnabled1->set(0.0);
1193     ProcessBuffer();
1194     assertNoMaster();
1195     assertSyncOff(m_sGroup1);
1196     assertSyncOff(m_sGroup2);
1197     EXPECT_DOUBLE_EQ(
1198             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1199 
1200     // Also works if neither deck is playing.
1201     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1202     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
1203     ControlObject::set(ConfigKey(m_sGroup2, "play"), 0.0);
1204     ProcessBuffer();
1205     pButtonSyncEnabled1->set(1.0);
1206     pButtonSyncEnabled1->set(0.0);
1207     ProcessBuffer();
1208     assertNoMaster();
1209     assertSyncOff(m_sGroup1);
1210     assertSyncOff(m_sGroup2);
1211     EXPECT_DOUBLE_EQ(
1212             130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1213 
1214     // But it doesn't work if deck 2 isn't playing and deck 1 is. (This would
1215     // cause deck1 to suddenly change bpm while playing back).
1216     ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0));
1217     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
1218     ControlObject::set(ConfigKey(m_sGroup2, "play"), 0.0);
1219     ProcessBuffer();
1220     pButtonSyncEnabled1->set(1.0);
1221     pButtonSyncEnabled1->set(0.0);
1222     ProcessBuffer();
1223     assertNoMaster();
1224     assertSyncOff(m_sGroup1);
1225     assertSyncOff(m_sGroup2);
1226     EXPECT_DOUBLE_EQ(
1227             100.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1228 }
1229 
TEST_F(EngineSyncTest,EjectTrackSyncRemains)1230 TEST_F(EngineSyncTest, EjectTrackSyncRemains) {
1231     auto pButtonSyncEnabled1 =
1232             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1233     auto pButtonSyncEnabled2 =
1234             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1235     auto pButtonEject1 = std::make_unique<ControlProxy>(m_sGroup1, "eject");
1236 
1237     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 120, 0.0);
1238     m_pTrack1->trySetBeats(pBeats1);
1239     pButtonSyncEnabled1->set(1.0);
1240     ProcessBuffer();
1241 
1242     // m_sGroup1 takes over
1243     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
1244     ASSERT_TRUE(isSoftMaster(m_sGroup1));
1245     assertSyncOff(m_sGroup2);
1246 
1247     pButtonEject1->set(1.0);
1248     // When an eject happens, the bpm gets set to zero.
1249     ProcessBuffer();
1250 
1251     EXPECT_DOUBLE_EQ(
1252             0.0, ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1253 
1254     // No valid tempo available all are waiting as follower
1255     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
1256     ASSERT_TRUE(isFollower(m_sGroup1));
1257     assertSyncOff(m_sGroup2);
1258 
1259     m_pMixerDeck1->loadFakeTrack(false, 128.0);
1260     EXPECT_DOUBLE_EQ(128.0,
1261             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1262     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 135, 0.0);
1263     m_pTrack2->trySetBeats(pBeats2);
1264     pButtonSyncEnabled2->set(1.0);
1265     ProcessBuffer();
1266 
1267     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
1268     ASSERT_TRUE(isFollower(m_sGroup1));
1269     ASSERT_TRUE(isFollower(m_sGroup2));
1270 
1271     pButtonEject1->set(1.0);
1272     m_pTrack1->trySetBeats(mixxx::BeatsPointer());
1273     ProcessBuffer();
1274 
1275     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
1276     ASSERT_TRUE(isFollower(m_sGroup1));
1277     ASSERT_TRUE(isSoftMaster(m_sGroup2));
1278 }
1279 
TEST_F(EngineSyncTest,FileBpmChangesDontAffectMaster)1280 TEST_F(EngineSyncTest, FileBpmChangesDontAffectMaster) {
1281     // If filebpm changes, don't treat it like a rate change.
1282     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 100, 0.0);
1283     m_pTrack1->trySetBeats(pBeats1);
1284     auto pButtonSyncEnabled1 =
1285             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1286     pButtonSyncEnabled1->set(1.0);
1287     ProcessBuffer();
1288 
1289     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
1290     m_pTrack2->trySetBeats(pBeats2);
1291     auto pButtonSyncEnabled2 =
1292             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1293     pButtonSyncEnabled2->set(1.0);
1294     ProcessBuffer();
1295 
1296     pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
1297     m_pTrack1->trySetBeats(pBeats1);
1298     EXPECT_DOUBLE_EQ(
1299             100.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
1300 }
1301 
TEST_F(EngineSyncTest,ExplicitMasterPostProcessed)1302 TEST_F(EngineSyncTest, ExplicitMasterPostProcessed) {
1303     // Regression test thanks to a bug.  Make sure that an explicit master
1304     // channel gets post-processed.
1305     auto pButtonMasterSync1 =
1306             std::make_unique<ControlProxy>(m_sGroup1, "sync_mode");
1307     pButtonMasterSync1->slotSet(SYNC_MASTER_EXPLICIT);
1308     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 160, 0.0);
1309     m_pTrack1->trySetBeats(pBeats1);
1310     ProcessBuffer();
1311     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1312     ProcessBuffer();
1313 
1314     EXPECT_NEAR(0.0023219956,
1315             m_pChannel1->getEngineBuffer()->getVisualPlayPos(),
1316             kMaxFloatingPointErrorLowPrecision);
1317 }
1318 
TEST_F(EngineSyncTest,ZeroBPMRateAdjustIgnored)1319 TEST_F(EngineSyncTest, ZeroBPMRateAdjustIgnored) {
1320     // If a track isn't loaded (0 bpm), but the deck has sync enabled,
1321     // don't pay attention to rate changes.
1322     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 0, 0.0);
1323     m_pTrack1->trySetBeats(pBeats1);
1324     auto pButtonSyncEnabled1 =
1325             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1326     pButtonSyncEnabled1->set(1.0);
1327     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1328             ->set(getRateSliderValue(1.0));
1329     ProcessBuffer();
1330 
1331     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 120, 0.0);
1332     m_pTrack2->trySetBeats(pBeats2);
1333     auto pButtonSyncEnabled2 =
1334             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1335     pButtonSyncEnabled2->set(1.0);
1336     ProcessBuffer();
1337 
1338     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1339             ->set(getRateSliderValue(1.3));
1340 
1341     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
1342             ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get());
1343 
1344     // Also try with explicit master/follower setting
1345     pButtonSyncEnabled1->set(0.0);
1346     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1347             ->set(SYNC_MASTER_EXPLICIT);
1348 
1349     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1350             ->set(getRateSliderValue(1.4));
1351     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
1352             ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get());
1353 
1354     pButtonSyncEnabled1->set(0.0);
1355     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1356             ->set(SYNC_FOLLOWER);
1357 
1358     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1359             ->set(getRateSliderValue(0.9));
1360     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
1361             ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get());
1362 }
1363 
TEST_F(EngineSyncTest,ZeroLatencyRateChangeNoQuant)1364 TEST_F(EngineSyncTest, ZeroLatencyRateChangeNoQuant) {
1365     // Confirm that a rate change in an explicit master is instantly communicated
1366     // to followers.
1367     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 128, 0.0);
1368     m_pTrack1->trySetBeats(pBeats1);
1369     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 160, 0.0);
1370     m_pTrack2->trySetBeats(pBeats2);
1371 
1372     // Make Channel2 master to weed out any channel ordering issues.
1373     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1374             ->set(SYNC_FOLLOWER);
1375     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1376             ->set(SYNC_FOLLOWER);
1377     // Exaggerate the effect with a high rate.
1378     ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 10.0);
1379 
1380     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1381     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1382 
1383     EXPECT_EQ(ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))
1384                       ->get(),
1385             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
1386                     ->get());
1387 
1388     for (int i = 0; i < 50; ++i) {
1389         ProcessBuffer();
1390         // Keep messing with the rate
1391         double rate = i % 2 == 0 ? i / 10.0 : i / -10.0;
1392         ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), rate);
1393 
1394         // Buffers should be in sync.
1395         ASSERT_NEAR(
1396                 ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1397                 ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1398                 kMaxBeatDistanceEpsilon);
1399     }
1400 
1401     // Make sure we're actually going somewhere!
1402     EXPECT_GT(
1403             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1404             0);
1405     // Buffers should be in sync.
1406     EXPECT_NEAR(
1407             ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1408             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1409             kMaxBeatDistanceEpsilon);
1410 }
1411 
TEST_F(EngineSyncTest,ZeroLatencyRateChangeQuant)1412 TEST_F(EngineSyncTest, ZeroLatencyRateChangeQuant) {
1413     // Confirm that a rate change in an explicit master is instantly communicated
1414     // to followers.
1415     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 128, 0.0);
1416     m_pTrack1->trySetBeats(pBeats1);
1417     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 160, 0.0);
1418     m_pTrack2->trySetBeats(pBeats2);
1419 
1420     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0);
1421     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
1422 
1423     // Make Channel2 master to weed out any channel ordering issues.
1424     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1425             ->set(SYNC_FOLLOWER);
1426     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1427             ->set(SYNC_FOLLOWER);
1428     // Exaggerate the effect with a high rate.
1429     ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 10.0);
1430 
1431     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1432     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1433 
1434     EXPECT_EQ(ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))
1435                       ->get(),
1436             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
1437                     ->get());
1438 
1439     ProcessBuffer();
1440 
1441     for (int i = 0; i < 50; ++i) {
1442         ProcessBuffer();
1443         // Keep messing with the rate
1444         double rate = i % 2 == 0 ? i / 10.0 : i / -10.0;
1445         ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), rate);
1446 
1447         // Buffers should be in sync.
1448         ASSERT_NEAR(
1449                 ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1450                 ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1451                 kMaxBeatDistanceEpsilon);
1452     }
1453 
1454     // Make sure we're actually going somewhere!
1455     EXPECT_GT(
1456             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1457             0);
1458     // Buffers should be in sync.
1459     EXPECT_NEAR(
1460             ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1461             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1462             kMaxBeatDistanceEpsilon);
1463 }
1464 
TEST_F(EngineSyncTest,ZeroLatencyRateDiffQuant)1465 TEST_F(EngineSyncTest, ZeroLatencyRateDiffQuant) {
1466     // Confirm that a rate change in an explicit master is instantly communicated
1467     // to followers.
1468     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 128, 0.0);
1469     m_pTrack1->trySetBeats(pBeats1);
1470     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 160, 0.0);
1471     m_pTrack2->trySetBeats(pBeats2);
1472 
1473     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
1474 
1475     // Make Channel2 master to weed out any channel ordering issues.
1476     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1477             ->set(SYNC_FOLLOWER);
1478     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1479             ->set(SYNC_FOLLOWER);
1480     // Exaggerate the effect with a high rate.
1481     ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 10.0);
1482 
1483     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1484     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1485 
1486     EXPECT_EQ(ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))
1487                       ->get(),
1488             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
1489                     ->get());
1490 
1491     ProcessBuffer();
1492 
1493     for (int i = 0; i < 50; ++i) {
1494         ProcessBuffer();
1495         // Keep messing with the rate
1496         double rate = i % 2 == 0 ? i / 10.0 : i / -10.0;
1497         ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), rate);
1498 
1499         // Buffers should be in sync.
1500         ASSERT_NEAR(
1501                 ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1502                 ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1503                 kMaxBeatDistanceEpsilon);
1504     }
1505 
1506     // Make sure we're actually going somewhere!
1507     EXPECT_GT(
1508             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1509             0);
1510     // Buffers should be in sync.
1511     EXPECT_NEAR(
1512             ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1513             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1514             kMaxBeatDistanceEpsilon);
1515 }
1516 
1517 // In this test, we set play *first* and then turn on master sync.
1518 // This exercises a slightly different ordering of signals that we
1519 // need to check. The Sync feature is unfortunately brittle.
1520 // This test exercises https://bugs.launchpad.net/mixxx/+bug/1884324
TEST_F(EngineSyncTest,ActivatingSyncDoesNotCauseDrifting)1521 TEST_F(EngineSyncTest, ActivatingSyncDoesNotCauseDrifting) {
1522     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 150, 0.0);
1523     m_pTrack1->trySetBeats(pBeats1);
1524     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 150, 0.0);
1525     m_pTrack2->trySetBeats(pBeats2);
1526 
1527     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(0.0);
1528     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
1529 
1530     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1531     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1532 
1533     ProcessBuffer();
1534 
1535     // make sure we aren't out-of-sync from the start
1536     EXPECT_EQ(ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))
1537                       ->get(),
1538             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
1539                     ->get());
1540 
1541     // engage first sync-master
1542     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1543             ->set(SYNC_FOLLOWER);
1544 
1545     // engage second Sync-master
1546     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1547             ->set(SYNC_FOLLOWER);
1548 
1549     // Run for a number of buffers
1550     for (int i = 0; i < 25; ++i) {
1551         ProcessBuffer();
1552     }
1553     // Make sure we're actually going somewhere!
1554     EXPECT_GT(ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
1555                       ->get(),
1556             0);
1557 
1558     // Buffers should be in sync.
1559     EXPECT_EQ(ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))
1560                       ->get(),
1561             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
1562                     ->get());
1563 }
1564 
TEST_F(EngineSyncTest,HalfDoubleBpmTest)1565 TEST_F(EngineSyncTest, HalfDoubleBpmTest) {
1566     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 70, 0.0);
1567     m_pTrack1->trySetBeats(pBeats1);
1568     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 140, 0.0);
1569     m_pTrack2->trySetBeats(pBeats2);
1570 
1571     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0);
1572     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
1573     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1574             ->set(SYNC_FOLLOWER);
1575     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1576             ->set(SYNC_FOLLOWER);
1577 
1578     // Mixxx will choose the first playing deck to be master.  Let's start deck 2 first.
1579     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1580     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1581     ProcessBuffer();
1582 
1583     EXPECT_EQ(0.5,
1584             m_pChannel1->getEngineBuffer()
1585                     ->m_pSyncControl->m_masterBpmAdjustFactor);
1586     EXPECT_EQ(1.0,
1587             m_pChannel2->getEngineBuffer()
1588                     ->m_pSyncControl->m_masterBpmAdjustFactor);
1589     EXPECT_DOUBLE_EQ(
1590             m_pChannel1->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1591             m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance());
1592     EXPECT_EQ(0, ControlObject::get(ConfigKey(m_sGroup1, "rate")));
1593     EXPECT_EQ(70, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
1594     EXPECT_EQ(0, ControlObject::get(ConfigKey(m_sGroup2, "rate")));
1595     EXPECT_EQ(140, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
1596 
1597     // Do lots of processing to make sure we get over the 0.5 beat_distance barrier.
1598     for (int i = 0; i < 50; ++i) {
1599         ProcessBuffer();
1600         // The beat distances are NOT as simple as x2 or /2.  Use the built-in functions
1601         // to do the proper conversion.
1602         EXPECT_NEAR(
1603                 m_pChannel1->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1604                 m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1605                 kMaxFloatingPointErrorLowPrecision);
1606     }
1607 
1608     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(0.0);
1609     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(0.0);
1610 
1611     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1612             ->set(SYNC_NONE);
1613     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1614             ->set(SYNC_NONE);
1615 
1616     EXPECT_EQ(1.0,
1617             m_pChannel1->getEngineBuffer()
1618                     ->m_pSyncControl->m_masterBpmAdjustFactor);
1619     EXPECT_EQ(1.0,
1620             m_pChannel2->getEngineBuffer()
1621                     ->m_pSyncControl->m_masterBpmAdjustFactor);
1622 
1623     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))
1624             ->set(SYNC_FOLLOWER);
1625     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))
1626             ->set(SYNC_FOLLOWER);
1627     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1628             ->set(getRateSliderValue(1.0));
1629 
1630     // Now start deck 1 first and check again.
1631     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1632     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1633 
1634     ProcessBuffer();
1635 
1636     EXPECT_EQ(1.0,
1637             m_pChannel1->getEngineBuffer()
1638                     ->m_pSyncControl->m_masterBpmAdjustFactor);
1639     EXPECT_EQ(2.0,
1640             m_pChannel2->getEngineBuffer()
1641                     ->m_pSyncControl->m_masterBpmAdjustFactor);
1642 
1643     // Exaggerate the effect with a high rate.
1644     ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))
1645             ->set(getRateSliderValue(2.0));
1646 
1647     for (int i = 0; i < 50; ++i) {
1648         ProcessBuffer();
1649         EXPECT_DOUBLE_EQ(m_pChannel1->getEngineBuffer()
1650                                  ->m_pSyncControl->getBeatDistance(),
1651                 m_pChannel2->getEngineBuffer()
1652                         ->m_pSyncControl->getBeatDistance());
1653     }
1654 }
1655 
TEST_F(EngineSyncTest,HalfDoubleThenPlay)1656 TEST_F(EngineSyncTest, HalfDoubleThenPlay) {
1657     // If a deck plays that had its multiplier set, we need to reset the
1658     // internal clock.
1659     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 80, 0.0);
1660     m_pTrack1->trySetBeats(pBeats1);
1661     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 175, 0.0);
1662     m_pTrack2->trySetBeats(pBeats2);
1663     ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))
1664             ->set(getRateSliderValue(1.0));
1665 
1666     ProcessBuffer();
1667 
1668     auto pButtonSyncEnabled1 =
1669             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1670     pButtonSyncEnabled1->slotSet(1.0);
1671     auto pButtonSyncEnabled2 =
1672             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1673     pButtonSyncEnabled2->slotSet(1.0);
1674     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0);
1675     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
1676 
1677     // We Expect that m_sGroup1 has adjusted its own bpm to the second deck and becomes a single master.
1678     // When the second deck is synced the master bpm is adopted by the interna clock, which becomes now
1679     // the master
1680     EXPECT_DOUBLE_EQ(87.5,
1681             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
1682                     ->get());
1683     EXPECT_DOUBLE_EQ(87.5,
1684             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1685     EXPECT_DOUBLE_EQ(175.0,
1686             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1687     EXPECT_DOUBLE_EQ(87.5 / 80,
1688             ControlObject::getControl(ConfigKey(m_sGroup1, "rate_ratio"))
1689                     ->get());
1690     EXPECT_DOUBLE_EQ(1,
1691             ControlObject::getControl(ConfigKey(m_sGroup2, "rate_ratio"))
1692                     ->get());
1693     EXPECT_DOUBLE_EQ(80,
1694             ControlObject::getControl(ConfigKey(m_sGroup1, "local_bpm"))
1695                     ->get());
1696     EXPECT_DOUBLE_EQ(175.0,
1697             ControlObject::getControl(ConfigKey(m_sGroup2, "local_bpm"))
1698                     ->get());
1699 
1700     ProcessBuffer();
1701 
1702     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1703     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1704 
1705     ProcessBuffer();
1706 
1707     EXPECT_DOUBLE_EQ(175.0,
1708             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
1709                     ->get());
1710 
1711     ProcessBuffer();
1712     ProcessBuffer();
1713     ProcessBuffer();
1714 
1715     EXPECT_DOUBLE_EQ(
1716             m_pChannel1->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1717             m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance());
1718 
1719     // Now enable the other deck first.
1720     // Sync only cares about which deck plays first now, enable order is irrelevant.
1721     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(0.0);
1722     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(0.0);
1723     pButtonSyncEnabled1->slotSet(0.0);
1724     pButtonSyncEnabled2->slotSet(0.0);
1725     ProcessBuffer();
1726     pButtonSyncEnabled2->slotSet(1.0);
1727     pButtonSyncEnabled1->slotSet(1.0);
1728     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1729     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1730 
1731     EXPECT_DOUBLE_EQ(87.5 / 80,
1732             ControlObject::getControl(ConfigKey(m_sGroup1, "rate_ratio"))
1733                     ->get());
1734     EXPECT_DOUBLE_EQ(1,
1735             ControlObject::getControl(ConfigKey(m_sGroup2, "rate_ratio"))
1736                     ->get());
1737 
1738     ProcessBuffer();
1739 
1740     EXPECT_NEAR(
1741             m_pChannel1->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1742             m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1743             kMaxFloatingPointErrorHighPrecision);
1744 
1745     ProcessBuffer();
1746     ProcessBuffer();
1747 
1748     EXPECT_DOUBLE_EQ(87.5,
1749             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1750 
1751     EXPECT_NEAR(
1752             m_pChannel1->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1753             m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1754             kMaxFloatingPointErrorHighPrecision);
1755 
1756     ProcessBuffer();
1757     ProcessBuffer();
1758     ProcessBuffer();
1759 
1760     EXPECT_NEAR(
1761             m_pChannel1->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1762             m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance(),
1763             kMaxFloatingPointErrorHighPrecision);
1764 }
1765 
TEST_F(EngineSyncTest,HalfDoubleInternalClockTest)1766 TEST_F(EngineSyncTest, HalfDoubleInternalClockTest) {
1767     // If we set the file_bpm CO's directly, the correct signals aren't fired.
1768     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 70, 0.0);
1769     m_pTrack1->trySetBeats(pBeats1);
1770     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 140, 0.0);
1771     m_pTrack2->trySetBeats(pBeats2);
1772 
1773     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0);
1774     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
1775     // Make Channel2 master to weed out any channel ordering issues.
1776     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1);
1777     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1);
1778 
1779     EXPECT_DOUBLE_EQ(70.0,
1780             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
1781                     ->get());
1782     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
1783             ControlObject::getControl(ConfigKey(m_sGroup1, "rate"))->get());
1784     EXPECT_DOUBLE_EQ(getRateSliderValue(1.0),
1785             ControlObject::getControl(ConfigKey(m_sGroup2, "rate"))->get());
1786 }
1787 
1788 namespace {
createBeatVector(double first_beat,unsigned int num_beats,double beat_length)1789 QVector<double> createBeatVector(
1790         double first_beat, unsigned int num_beats, double beat_length) {
1791     QVector<double> beats;
1792     for (unsigned int i = 0; i < num_beats; ++i) {
1793         beats.append(first_beat + i * beat_length);
1794     }
1795     return beats;
1796 }
1797 } // namespace
1798 
TEST_F(EngineSyncTest,HalfDoubleConsistency)1799 TEST_F(EngineSyncTest, HalfDoubleConsistency) {
1800     // half-double matching should be consistent
1801     double beatLengthFrames = 60.0 * 44100 / 90.0;
1802     double startOffsetFrames = 0;
1803     const int numBeats = 100;
1804     QVector<double> beats1 =
1805             createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
1806     auto pBeats1 = mixxx::BeatMap::makeBeatMap(m_pTrack1->getSampleRate(), QString(), beats1);
1807     m_pTrack1->trySetBeats(pBeats1);
1808 
1809     beatLengthFrames = 60.0 * 44100 / 145.0;
1810     QVector<double> beats2 =
1811             createBeatVector(startOffsetFrames, numBeats, beatLengthFrames);
1812     auto pBeats2 = mixxx::BeatMap::makeBeatMap(m_pTrack2->getSampleRate(), QString(), beats2);
1813     m_pTrack2->trySetBeats(pBeats2);
1814 
1815     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1816     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1);
1817     EXPECT_DOUBLE_EQ(180.0,
1818             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1819 
1820     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1821     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1);
1822     EXPECT_DOUBLE_EQ(90.0,
1823             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1824     EXPECT_DOUBLE_EQ(180.0,
1825             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1826 
1827     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(0);
1828     ProcessBuffer();
1829     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1);
1830     ProcessBuffer();
1831     EXPECT_DOUBLE_EQ(90.0,
1832             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1833     EXPECT_DOUBLE_EQ(180.0,
1834             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1835 }
1836 
TEST_F(EngineSyncTest,HalfDoubleEachOther)1837 TEST_F(EngineSyncTest, HalfDoubleEachOther) {
1838     // Confirm that repeated sync with both decks leads to the same
1839     // Half/Double decision.
1840     // This test demonstrates https://bugs.launchpad.net/mixxx/+bug/1921962
1841     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 144, 0.0);
1842     m_pTrack1->trySetBeats(pBeats1);
1843     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 105, 0.0);
1844     m_pTrack2->trySetBeats(pBeats2);
1845 
1846     // Threshold 1.414 sqrt(2);
1847     // 144 / 105 = 1.37
1848     // 105 / 72 = 1.46
1849     // expect 144 BPM
1850 
1851     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1.0);
1852     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(0.0);
1853 
1854     EXPECT_DOUBLE_EQ(144.0,
1855             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1856 
1857     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1.0);
1858     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(0.0);
1859 
1860     EXPECT_DOUBLE_EQ(144.0,
1861             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1862 
1863     // Threshold 1.414 sqrt(2);
1864     // 150 / 105 = 1.43
1865     // 105 / 75 = 1.40
1866     // expect 75 BPM
1867 
1868     mixxx::BeatsPointer pBeats1b = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 150, 0.0);
1869     m_pTrack1->trySetBeats(pBeats1b);
1870 
1871     EXPECT_DOUBLE_EQ(150.0,
1872             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1873 
1874     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1.0);
1875     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(0.0);
1876 
1877     EXPECT_DOUBLE_EQ(75.0,
1878             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1879 
1880     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1.0);
1881     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(0.0);
1882 
1883     EXPECT_DOUBLE_EQ(150.0,
1884             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1885 }
1886 
TEST_F(EngineSyncTest,SetFileBpmUpdatesLocalBpm)1887 TEST_F(EngineSyncTest, SetFileBpmUpdatesLocalBpm) {
1888     ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->set(0.2);
1889     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
1890     m_pTrack1->trySetBeats(pBeats1);
1891     ASSERT_EQ(
1892             130.0, m_pEngineSync->getSyncableForGroup(m_sGroup1)->getBaseBpm());
1893 }
1894 
TEST_F(EngineSyncTest,SyncPhaseToPlayingNonSyncDeck)1895 TEST_F(EngineSyncTest, SyncPhaseToPlayingNonSyncDeck) {
1896     // If we press play on a sync deck, we will only sync phase to a non-sync
1897     // deck if there are no sync decks and the non-sync deck is playing.
1898 
1899     auto pButtonSyncEnabled1 =
1900             std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
1901     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
1902     m_pTrack1->trySetBeats(pBeats1);
1903     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0);
1904 
1905     auto pButtonSyncEnabled2 =
1906             std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
1907     ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 1.0);
1908     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 100, 0.0);
1909     m_pTrack2->trySetBeats(pBeats2);
1910 
1911     // Set the sync deck playing with nothing else active.
1912     // Next Deck becomes master and the Master clock is set to 100 BPM
1913     // The 130 BPM Track should be played at 100 BPM, rate = 0,769230769
1914     pButtonSyncEnabled1->set(1.0);
1915     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1916     // Beat length: 40707.692307692305
1917     // Buffer size is 1024 at 44.1 kHz at the given rate = 787.692307692 Samples
1918     // Expected beat_distance = 0.019349962
1919     ProcessBuffer();
1920 
1921     // Internal clock rate should be set and beat distance already updated
1922     EXPECT_DOUBLE_EQ(100.0,
1923             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1924     EXPECT_NEAR(0.019349962,
1925             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1926             kMaxFloatingPointErrorLowPrecision);
1927 
1928     // The internal clock must also have been advanced to the same fraction of a beat.
1929     EXPECT_DOUBLE_EQ(100.0,
1930             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))->get());
1931     EXPECT_NEAR(0.019349962,
1932             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "beat_distance"))->get(),
1933             kMaxFloatingPointErrorLowPrecision);
1934 
1935     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(0.0);
1936 
1937     ProcessBuffer();
1938 
1939     // we expect that Deck 1 distance has not changed and the internal clock has continued
1940     // with the same rate
1941     EXPECT_NEAR(0.019349962,
1942             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get(),
1943             kMaxFloatingPointErrorLowPrecision);
1944     EXPECT_NEAR(0.019349962,
1945             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "beat_distance"))->get(),
1946             kMaxFloatingPointErrorLowPrecision);
1947 
1948     // Now make the second deck playing and see if it works.
1949     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
1950     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
1951 
1952     // What seems to be happening is a seek is queued when a track is loaded, so all these tracks
1953     // seek to 0, thus resetting the beat distance.
1954     ProcessBuffer();
1955 
1956     // We expect that the second deck (master) has adjusted the "beat_distance" of the
1957     // internal clock and the fist deck is advanced slightly but slower to slowly get
1958     // rid of the additional buffer ahead
1959     // TODO: It does sounds odd to start the track 1 at a random position and adjust the
1960     // phase later. Seeking into phase is the best option even with quantize off.
1961     EXPECT_DOUBLE_EQ(100.0,
1962             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
1963     // The adjustment is calculated here: BpmControl::calcSyncAdjustment
1964     EXPECT_GT(0.038699925, ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->get());
1965     EXPECT_DOUBLE_EQ(100.0,
1966             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
1967     EXPECT_NEAR(
1968             0.019349962,
1969             ControlObject::getControl(ConfigKey(m_sGroup2, "beat_distance"))->get(),
1970             kMaxFloatingPointErrorLowPrecision);
1971     EXPECT_DOUBLE_EQ(100.0,
1972             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))->get());
1973     EXPECT_NEAR(
1974             0.038699925,
1975             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "beat_distance"))->get(),
1976             kMaxFloatingPointErrorLowPrecision);
1977 
1978     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
1979     pButtonSyncEnabled1->set(0.0);
1980     ControlObject::set(ConfigKey(m_sGroup1, "rate_ratio"), 1.0);
1981 
1982     ControlObject::set(ConfigKey(m_sGroup2, "play"), 0.0);
1983     pButtonSyncEnabled2->set(0.0);
1984     ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 1.0);
1985 
1986     // But if there is a third deck that is sync-enabled, we match that.
1987     auto pButtonSyncEnabled3 =
1988             std::make_unique<ControlProxy>(m_sGroup3, "sync_enabled");
1989     ControlObject::set(ConfigKey(m_sGroup3, "beat_distance"), 0.6);
1990     ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 1.0);
1991     mixxx::BeatsPointer pBeats3 = BeatFactory::makeBeatGrid(m_pTrack3->getSampleRate(), 140, 0.0);
1992     m_pTrack3->trySetBeats(pBeats3);
1993     // This will sync to the first deck here and not the second (lp1784185)
1994     pButtonSyncEnabled3->set(1.0);
1995     ProcessBuffer();
1996     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup3, "bpm")));
1997     // revert that
1998     ControlObject::set(ConfigKey(m_sGroup3, "rate_ratio"), 1.0);
1999     ProcessBuffer();
2000     EXPECT_DOUBLE_EQ(140.0, ControlObject::get(ConfigKey(m_sGroup3, "bpm")));
2001     // now we have Deck 3 with 140 bpm and sync enabled
2002 
2003     pButtonSyncEnabled1->set(1.0);
2004     ProcessBuffer();
2005 
2006     ControlObject::getControl(ConfigKey(m_sGroup3, "play"))->set(1.0);
2007     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
2008     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
2009     ProcessBuffer();
2010 
2011     // We expect Deck 1 is Deck 3 bpm
2012     EXPECT_DOUBLE_EQ(140.0,
2013             ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))
2014                     ->get());
2015     EXPECT_DOUBLE_EQ(140.0,
2016             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
2017     // The exact beat distance will be one buffer past .6, but this is good
2018     // enough to confirm that it worked.
2019     EXPECT_GT(0.7,
2020             ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
2021                     ->get());
2022     EXPECT_GT(0.7,
2023             ControlObject::getControl(
2024                     ConfigKey(m_sInternalClockGroup, "beat_distance"))
2025                     ->get());
2026 }
2027 
TEST_F(EngineSyncTest,UserTweakBeatDistance)2028 TEST_F(EngineSyncTest, UserTweakBeatDistance) {
2029     // If a deck has a user tweak, and another deck stops such that the first
2030     // is used to reseed the master beat distance, make sure the user offset
2031     // is reset.
2032     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 128, 0.0);
2033     m_pTrack1->trySetBeats(pBeats1);
2034     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 128, 0.0);
2035     m_pTrack2->trySetBeats(pBeats2);
2036 
2037     ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0);
2038     ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0);
2039     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1);
2040     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1);
2041     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
2042     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
2043 
2044     // Play some buffers with the wheel at 0 to show the sync works
2045     ControlObject::getControl(ConfigKey(m_sGroup1, "wheel"))->set(0);
2046     for (int i = 0; i < 10; ++i) {
2047         ProcessBuffer();
2048     }
2049 
2050     // Spin the wheel, causing the useroffset for group1 to get set.
2051     ControlObject::getControl(ConfigKey(m_sGroup1, "wheel"))->set(0.4);
2052     for (int i = 0; i < 10; ++i) {
2053         ProcessBuffer();
2054     }
2055     // Play some more buffers with the wheel at 0.
2056     ControlObject::getControl(ConfigKey(m_sGroup1, "wheel"))->set(0);
2057     for (int i = 0; i < 10; ++i) {
2058         ProcessBuffer();
2059     }
2060 
2061     // Stop the second deck.  This causes the master beat distance to get
2062     // seeded with the beat distance from deck 1.
2063     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(0.0);
2064 
2065     // Play a buffer, which is enough to see if the beat distances align.
2066     ProcessBuffer();
2067 
2068     EXPECT_NEAR(ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))
2069                         ->get(),
2070             ControlObject::getControl(
2071                     ConfigKey(m_sInternalClockGroup, "beat_distance"))
2072                     ->get(),
2073             .00001);
2074 
2075     EXPECT_DOUBLE_EQ(0.0,
2076             m_pChannel1->getEngineBuffer()
2077                     ->m_pBpmControl->m_dUserOffset.getValue());
2078 }
2079 
TEST_F(EngineSyncTest,UserTweakPreservedInSeek)2080 TEST_F(EngineSyncTest, UserTweakPreservedInSeek) {
2081     // Ensure that when we do a seek during master sync, the user offset is maintained.
2082 
2083     // This is about 128 bpm, but results in nice round numbers of samples.
2084     const double kDivisibleBpm = 44100.0 / 344.0;
2085     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(
2086             m_pTrack1->getSampleRate(), kDivisibleBpm, 0.0);
2087     m_pTrack1->trySetBeats(pBeats1);
2088     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 130, 0.0);
2089     m_pTrack2->trySetBeats(pBeats2);
2090 
2091     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1);
2092     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_enabled"))->set(1);
2093     ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0);
2094     ControlObject::set(ConfigKey(m_sGroup2, "quantize"), 1.0);
2095     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2096     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
2097 
2098     ProcessBuffer();
2099     EXPECT_DOUBLE_EQ(kDivisibleBpm,
2100             ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get());
2101     EXPECT_DOUBLE_EQ(kDivisibleBpm,
2102             ControlObject::getControl(ConfigKey(m_sGroup2, "bpm"))->get());
2103 
2104     EXPECT_NEAR(0.024806201,
2105             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
2106             kMaxFloatingPointErrorLowPrecision);
2107     EXPECT_NEAR(0.0023219956,
2108             ControlObject::get(ConfigKey(m_sGroup1, "playposition")),
2109             kMaxFloatingPointErrorLowPrecision);
2110 
2111     ControlObject::set(ConfigKey(m_sGroup2, "playposition"), 0.2);
2112     ProcessBuffer();
2113     ControlObject::set(ConfigKey(m_sGroup1, "playposition"), 0.2);
2114     ProcessBuffer();
2115 
2116     // We are a few buffers past the seeked position.  It's less than 0.2 because
2117     // of quantizing.  The playpositions are different because the tracks are at
2118     // different bpms.
2119     EXPECT_NEAR(0.19417687,
2120             ControlObject::get(ConfigKey(m_sGroup1, "playposition")),
2121             kMaxFloatingPointErrorLowPrecision);
2122     EXPECT_NEAR(0.19148479,
2123             ControlObject::get(ConfigKey(m_sGroup2, "playposition")),
2124             kMaxFloatingPointErrorLowPrecision);
2125     // The beat distances are identical though.
2126     EXPECT_NEAR(0.074418604,
2127             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
2128             kMaxFloatingPointErrorLowPrecision);
2129     EXPECT_NEAR(0.074418604,
2130             ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")),
2131             kMaxFloatingPointErrorLowPrecision);
2132 
2133     // Apply user tweak offset.
2134     m_pChannel1->getEngineBuffer()
2135             ->m_pBpmControl->m_dUserOffset.setValue(0.3);
2136 
2137     // Seek back to 0.2
2138     ControlObject::set(ConfigKey(m_sGroup2, "playposition"), 0.2);
2139     ProcessBuffer();
2140     ControlObject::set(ConfigKey(m_sGroup1, "playposition"), 0.2);
2141     ProcessBuffer();
2142 
2143     // Now, the locations are much more different - but the beat distance appears
2144     // to be the same because beat distance CO hides the user offset.
2145     EXPECT_NEAR(0.2269025,
2146             ControlObject::get(ConfigKey(m_sGroup1, "playposition")),
2147             kMaxFloatingPointErrorLowPrecision);
2148     EXPECT_NEAR(0.1960644,
2149             ControlObject::get(ConfigKey(m_sGroup2, "playposition")),
2150             kMaxFloatingPointErrorLowPrecision);
2151     EXPECT_NEAR(0.12403101,
2152             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
2153             kMaxFloatingPointErrorLowPrecision);
2154     EXPECT_NEAR(0.12403101,
2155             ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")),
2156             kMaxFloatingPointErrorLowPrecision);
2157 }
2158 
TEST_F(EngineSyncTest,MasterBpmNeverZero)2159 TEST_F(EngineSyncTest, MasterBpmNeverZero) {
2160     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 128, 0.0);
2161     m_pTrack1->trySetBeats(pBeats1);
2162 
2163     auto pButtonSyncEnabled1 = std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
2164     pButtonSyncEnabled1->set(1.0);
2165 
2166     m_pTrack1->trySetBeats(mixxx::BeatsPointer());
2167     EXPECT_EQ(128.0,
2168               ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm"))->get());
2169 }
2170 
TEST_F(EngineSyncTest,ZeroBpmNaturalRate)2171 TEST_F(EngineSyncTest, ZeroBpmNaturalRate) {
2172     // If a track has a zero bpm and a bad beatgrid, make sure the rate
2173     // doesn't end up something crazy when sync is enabled..
2174     // Maybe the beatgrid ended up at zero also.
2175     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 0.0, 0.0);
2176     m_pTrack1->trySetBeats(pBeats1);
2177 
2178     auto pButtonSyncEnabled1 = std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
2179     pButtonSyncEnabled1->set(1.0);
2180 
2181     ProcessBuffer();
2182 
2183     // 0 bpm is what we want, the sync code will play the track back at rate
2184     // 1.0.
2185     EXPECT_EQ(0.0,
2186               ControlObject::getControl(ConfigKey(m_sGroup1, "local_bpm"))->get());
2187 }
2188 
TEST_F(EngineSyncTest,QuantizeImpliesSyncPhase)2189 TEST_F(EngineSyncTest, QuantizeImpliesSyncPhase) {
2190     auto pButtonSyncEnabled1 = std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
2191     auto pButtonBeatsync1 = std::make_unique<ControlProxy>(m_sGroup1, "beatsync");
2192     auto pButtonBeatsyncPhase1 = std::make_unique<ControlProxy>(m_sGroup1, "beatsync_phase");
2193 
2194     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
2195     m_pTrack1->trySetBeats(pBeats1);
2196 
2197     ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0));
2198     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 100, 0.0);
2199     m_pTrack2->trySetBeats(pBeats2);
2200 
2201     ProcessBuffer();
2202 
2203     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2204     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
2205     ProcessBuffer();
2206 
2207     // Beat length: 40707.692307692; Buffer size is 1024
2208     // Expected beat_distance = 1024/40707 = 0.025155
2209     EXPECT_DOUBLE_EQ(130, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2210     EXPECT_DOUBLE_EQ(0.025154950869236584, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2211 
2212     // Beat length: 52920; Buffer size is 1024
2213     // Expected beat_distance = 0.01935
2214     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2215     EXPECT_DOUBLE_EQ(0.019349962207105064, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2216 
2217     // first test without quantization
2218     pButtonSyncEnabled1->set(1.0);
2219     ProcessBuffer();
2220 
2221     // Deck 1 continues with 100 bpm
2222     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
2223     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2224     EXPECT_DOUBLE_EQ(0.044504913076341648, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2225 
2226     // Deck 2 continues normally
2227     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2228     EXPECT_DOUBLE_EQ(0.038699924414210128, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2229 
2230     pButtonSyncEnabled1->set(0.0);
2231 
2232     ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0);
2233     ProcessBuffer();
2234 
2235     // Quantize only has no effect here, both decks are running freely.
2236 
2237     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2238     EXPECT_DOUBLE_EQ(0.063854875283446716, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2239 
2240     // Deck 2 continues normally
2241     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2242     EXPECT_DOUBLE_EQ(0.058049886621315196, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2243     pButtonSyncEnabled1->set(1.0);
2244     ProcessBuffer();
2245 
2246     // normally the deck would be at 0.083204837, due to quantize it has been seeked
2247     // in phase of deck 2
2248     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2249     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2250 
2251     EXPECT_NEAR(
2252             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
2253             ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")),
2254             1e-15);
2255 
2256     // Reset Deck 1 Tempo
2257     pButtonSyncEnabled1->set(0.0);
2258     ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 0.0);
2259     ControlObject::set(ConfigKey(m_sGroup1, "rate_ratio"), 1.0);
2260     ProcessBuffer();
2261 
2262     // Now the deck should be running normally
2263     EXPECT_DOUBLE_EQ(130, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2264     EXPECT_NEAR(
2265             0.10255479969765675,
2266             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")),
2267             1e-15);
2268 
2269     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2270     EXPECT_DOUBLE_EQ(0.096749811035525324, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2271 
2272     pButtonBeatsyncPhase1->set(1.0);
2273     ProcessBuffer();
2274 
2275     // check if the next beat will be aligned:
2276 
2277     EXPECT_DOUBLE_EQ(130, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2278     EXPECT_DOUBLE_EQ(100, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2279 
2280     // we align here to the past beat, because beat_distance < 1.0/8
2281     EXPECT_NEAR(
2282             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")) / 130 * 100,
2283             ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")),
2284             1e-15);
2285 }
2286 
TEST_F(EngineSyncTest,SeekStayInPhase)2287 TEST_F(EngineSyncTest, SeekStayInPhase) {
2288     ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0);
2289 
2290     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
2291     m_pTrack1->trySetBeats(pBeats1);
2292 
2293     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2294     ProcessBuffer();
2295 
2296     EXPECT_DOUBLE_EQ(0.025154950869236584, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2297     EXPECT_DOUBLE_EQ(0.0023219954648526077, ControlObject::get(ConfigKey(m_sGroup1, "playposition")));
2298 
2299     ControlObject::set(ConfigKey(m_sGroup1, "playposition"), 0.2);
2300     ProcessBuffer();
2301 
2302     // We expect to be two buffers ahead in a beat near 0.2
2303     EXPECT_DOUBLE_EQ(0.050309901738473162,
2304             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2305     EXPECT_DOUBLE_EQ(0.18925937554508981, ControlObject::get(ConfigKey(m_sGroup1, "playposition")));
2306 
2307     // The same again with a stopped track loaded in Channel 2
2308     ControlObject::set(ConfigKey(m_sGroup1, "playposition"), 0.0);
2309     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
2310     ProcessBuffer();
2311 
2312     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
2313     m_pTrack2->trySetBeats(pBeats2);
2314 
2315     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2316     ProcessBuffer();
2317 
2318     EXPECT_DOUBLE_EQ(0.025154950869236584,
2319             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2320     EXPECT_DOUBLE_EQ(0.0023219954648526077,
2321             ControlObject::get(ConfigKey(m_sGroup1, "playposition")));
2322 
2323     ControlObject::set(ConfigKey(m_sGroup1, "playposition"), 0.2);
2324     ProcessBuffer();
2325 
2326     // We expect to be two buffers ahead in a beat near 0.2
2327     EXPECT_DOUBLE_EQ(0.050309901738473162,
2328             ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2329     EXPECT_DOUBLE_EQ(0.18925937554508981, ControlObject::get(ConfigKey(m_sGroup1, "playposition")));
2330 }
2331 
TEST_F(EngineSyncTest,SyncWithoutBeatgrid)2332 TEST_F(EngineSyncTest, SyncWithoutBeatgrid) {
2333     // this tests bug lp1783020, notresetting rate when other deck has no beatgrid
2334     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 128, 0.0);
2335     m_pTrack1->trySetBeats(pBeats1);
2336     m_pTrack2->trySetBeats(mixxx::BeatsPointer());
2337 
2338     ControlObject::set(ConfigKey(m_sGroup1, "rate"), 0.5);
2339 
2340     // Play a buffer, which is enough to see if the beat distances align.
2341     ProcessBuffer();
2342 
2343     ControlObject::set(ConfigKey(m_sGroup1, "sync_enabled"), 1);
2344 
2345     ProcessBuffer();
2346 
2347     ASSERT_DOUBLE_EQ(0.5,
2348               ControlObject::get(ConfigKey(m_sGroup1, "rate")));
2349 
2350 }
2351 
TEST_F(EngineSyncTest,QuantizeHotCueActivate)2352 TEST_F(EngineSyncTest, QuantizeHotCueActivate) {
2353     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
2354     m_pTrack1->trySetBeats(pBeats1);
2355 
2356     auto pHotCue2Activate = std::make_unique<ControlProxy>(m_sGroup2, "hotcue_1_activate");
2357     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 100, 0.0);
2358 
2359     m_pTrack2->trySetBeats(pBeats2);
2360 
2361     ProcessBuffer();
2362 
2363     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2364 
2365     // store a hot cue at 0:00
2366     pHotCue2Activate->set(1.0);
2367     ProcessBuffer();
2368     pHotCue2Activate->set(0.0);
2369     ProcessBuffer();
2370 
2371     ControlObject::set(ConfigKey(m_sGroup2, "quantize"), 0.0);
2372     // preview a hot cue without quantize
2373     pHotCue2Activate->set(1.0);
2374     ProcessBuffer();
2375 
2376     // Expect that the track has advanced by one buffer starting from the hot cue at 0:00
2377     EXPECT_DOUBLE_EQ(0.019349962207105064, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2378 
2379     pHotCue2Activate->set(0.0);
2380     ProcessBuffer();
2381 
2382     // Expect that the track was set back to 0:00
2383     EXPECT_DOUBLE_EQ(0, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2384 
2385     // Expect that the first track has advanced 4 buffers in the mean time
2386     EXPECT_DOUBLE_EQ(0.10061980347694634, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2387 
2388     ControlObject::set(ConfigKey(m_sGroup2, "quantize"), 1.0);
2389     // preview a hot cue with quantize seeks back to 0:00 and adjust beat distance to match the first track
2390     pHotCue2Activate->set(1.0);
2391     ProcessBuffer();
2392 
2393     // Beat_distance is the distance to the previous beat wich has already passed.
2394     // We compare here the distance to the next beat (1 - beat_distance) and
2395     // scale it by the different tempos.
2396     EXPECT_NEAR(
2397             (1 - ControlObject::get(ConfigKey(m_sGroup1, "beat_distance"))) / 130 * 100,
2398             (1 - ControlObject::get(ConfigKey(m_sGroup2, "beat_distance"))),
2399             1e-15);
2400 
2401     pHotCue2Activate->set(0.0);
2402     ProcessBuffer();
2403 
2404     // Expect that the track is back to 0:00
2405     EXPECT_DOUBLE_EQ(0, ControlObject::get(ConfigKey(m_sGroup2, "beat_distance")));
2406 
2407     // Expect that the first track has advanced 6 buffers in the mean time
2408     EXPECT_DOUBLE_EQ(0.15092970521541951, ControlObject::get(ConfigKey(m_sGroup1, "beat_distance")));
2409 }
2410 
TEST_F(EngineSyncTest,ChangeBeatGrid)2411 TEST_F(EngineSyncTest, ChangeBeatGrid) {
2412     // https://bugs.launchpad.net/mixxx/+bug/1808698
2413 
2414     auto pButtonSyncEnabled1 = std::make_unique<ControlProxy>(m_sGroup1, "sync_enabled");
2415     auto pButtonSyncEnabled2 = std::make_unique<ControlProxy>(m_sGroup2, "sync_enabled");
2416 
2417     // set beatgrid
2418     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 130, 0.0);
2419     m_pTrack1->trySetBeats(pBeats1);
2420     pButtonSyncEnabled1->set(1.0);
2421     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2422 
2423     ProcessBuffer();
2424 
2425     ASSERT_TRUE(isSoftMaster(m_sGroup1));
2426     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2427     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
2428 
2429     // sync 0 bpm track to the first one
2430     pButtonSyncEnabled2->set(1.0);
2431     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
2432 
2433     ProcessBuffer();
2434 
2435     // expect no change in Deck 1
2436     ASSERT_TRUE(isSoftMaster(m_sGroup1));
2437     ASSERT_TRUE(isFollower(m_sGroup2));
2438     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2439     EXPECT_DOUBLE_EQ(0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2440     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
2441 
2442     ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0);
2443 
2444     ProcessBuffer();
2445     // Group1 remains master because it is the only one with a tempo,
2446     ASSERT_TRUE(isSoftMaster(m_sGroup1));
2447     ASSERT_TRUE(isFollower(m_sGroup2));
2448 
2449     // Load a new beatgrid during playing, this happens when the analyser is finished
2450     mixxx::BeatsPointer pBeats2 = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 140, 0.0);
2451     m_pTrack2->trySetBeats(pBeats2);
2452 
2453     ProcessBuffer();
2454 
2455     // we expect that the new beatgrid is aligned to the other playing track
2456     ASSERT_TRUE(isSoftMaster(m_sGroup2));
2457     ASSERT_TRUE(isFollower(m_sGroup1));
2458     ASSERT_TRUE(isFollower(m_sInternalClockGroup));
2459     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2460     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2461     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
2462 
2463     // Play Both Tracks.
2464     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2465     ProcessBuffer();
2466 
2467     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
2468     ASSERT_TRUE(isFollower(m_sGroup1));
2469     ASSERT_TRUE(isFollower(m_sGroup2));
2470 
2471     // Load a new beatgrid again, this happens when the user adjusts the beatgrid
2472     mixxx::BeatsPointer pBeats2n = BeatFactory::makeBeatGrid(m_pTrack2->getSampleRate(), 75, 0.0);
2473     m_pTrack2->trySetBeats(pBeats2n);
2474 
2475     ProcessBuffer();
2476 
2477     // we expect that the new beatgrid is aligned to the other playing track
2478     // Not the case before fixing lp1808698
2479     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2480     // Expect to sync on half beats
2481     EXPECT_DOUBLE_EQ(65.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2482     EXPECT_DOUBLE_EQ(130.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm")));
2483 }
2484 
TEST_F(EngineSyncTest,BeatMapQantizePlay)2485 TEST_F(EngineSyncTest, BeatMapQantizePlay) {
2486     // This test demonstates https://bugs.launchpad.net/mixxx/+bug/1874918
2487     mixxx::BeatsPointer pBeats1 = BeatFactory::makeBeatGrid(m_pTrack1->getSampleRate(), 120, 0.0);
2488     m_pTrack1->trySetBeats(pBeats1);
2489 
2490     constexpr auto kSampleRate = mixxx::audio::SampleRate(44100);
2491 
2492     auto pBeats2 = mixxx::BeatMap::makeBeatMap(kSampleRate,
2493             QString(),
2494             // Add two beats at 120 Bpm
2495             QVector<double>({static_cast<double>(kSampleRate) / 2,
2496                     static_cast<double>(kSampleRate)}));
2497     m_pTrack2->trySetBeats(pBeats2);
2498 
2499     ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0);
2500     ControlObject::set(ConfigKey(m_sGroup2, "quantize"), 1.0);
2501 
2502     ControlObject::getControl(ConfigKey(m_sGroup1, "sync_mode"))->set(SYNC_MASTER_EXPLICIT);
2503     ControlObject::getControl(ConfigKey(m_sGroup2, "sync_mode"))->set(SYNC_FOLLOWER);
2504 
2505     ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0);
2506 
2507     ProcessBuffer();
2508 
2509     ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0);
2510 
2511     ProcessBuffer();
2512 
2513     // Beat Distance shall be still 0, because we are before the first beat.
2514     // This was fixed in https://bugs.launchpad.net/mixxx/+bug/1920084
2515     EXPECT_DOUBLE_EQ(m_pChannel2->getEngineBuffer()->m_pSyncControl->getBeatDistance(), 0);
2516     EXPECT_DOUBLE_EQ(
2517             ControlObject::get(ConfigKey(m_sGroup1, "playposition")),
2518             ControlObject::get(ConfigKey(m_sGroup2, "playposition")));
2519 }
2520 
TEST_F(EngineSyncTest,BpmAdjustFactor)2521 TEST_F(EngineSyncTest, BpmAdjustFactor) {
2522     // Failing test case from https://github.com/mixxxdj/mixxx/pull/2376
2523 
2524     m_pMixerDeck1->loadFakeTrack(false, 40.0);
2525     m_pMixerDeck2->loadFakeTrack(false, 150.0);
2526     ProcessBuffer();
2527 
2528     EXPECT_DOUBLE_EQ(40.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2529     EXPECT_DOUBLE_EQ(150.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2530 
2531     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2532     ProcessBuffer();
2533 
2534     ControlObject::set(ConfigKey(m_sGroup2, "sync_enabled"), 1.0);
2535     ProcessBuffer();
2536 
2537     // group 2 should be synced to the first playing deck and becomes master
2538     EXPECT_DOUBLE_EQ(40.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2539     EXPECT_DOUBLE_EQ(80.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2540     ASSERT_TRUE(isSoftMaster(m_sGroup2));
2541     assertSyncOff(m_sGroup1);
2542 
2543     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
2544     ProcessBuffer();
2545     ASSERT_TRUE(isSoftMaster(m_sGroup2));
2546     // Pretend a changing beatgrid
2547     static_cast<SyncControl*>(m_pEngineSync->getMasterSyncable())->setLocalBpm(152);
2548     ProcessBuffer();
2549 
2550     ControlObject::set(ConfigKey(m_sGroup1, "sync_enabled"), 1.0);
2551     ProcessBuffer();
2552 
2553     // Group 1 should be already in sync, it must not change due to sync
2554     // and Group 2 must also remain.
2555     EXPECT_DOUBLE_EQ(40.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2556     EXPECT_DOUBLE_EQ(80.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2557     ASSERT_TRUE(isFollower(m_sGroup1));
2558     ASSERT_TRUE(isFollower(m_sGroup2));
2559     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
2560 }
2561 
TEST_F(EngineSyncTest,ImpliciteMasterToInternalClock)2562 TEST_F(EngineSyncTest, ImpliciteMasterToInternalClock) {
2563     m_pMixerDeck1->loadFakeTrack(false, 40.0);
2564     m_pMixerDeck2->loadFakeTrack(false, 150.0);
2565     ProcessBuffer();
2566 
2567     EXPECT_DOUBLE_EQ(40.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2568     EXPECT_DOUBLE_EQ(150.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2569 
2570     // During cue-ing volume is 0.0
2571     ControlObject::set(ConfigKey(m_sGroup1, "volume"), 0.0);
2572     ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0);
2573     ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0);
2574     ProcessBuffer();
2575 
2576     ControlObject::set(ConfigKey(m_sGroup1, "sync_enabled"), 1.0);
2577     ControlObject::set(ConfigKey(m_sGroup2, "sync_enabled"), 1.0);
2578     ProcessBuffer();
2579 
2580     // group 2 should be synced to the first playing deck and becomes master
2581     EXPECT_DOUBLE_EQ(75.0, ControlObject::get(ConfigKey(m_sGroup1, "bpm")));
2582     EXPECT_DOUBLE_EQ(150.0, ControlObject::get(ConfigKey(m_sGroup2, "bpm")));
2583     ASSERT_FALSE(isSoftMaster(m_sGroup1));
2584     ASSERT_TRUE(isSoftMaster(m_sGroup2));
2585     ASSERT_FALSE(isSoftMaster(m_sInternalClockGroup));
2586     ProcessBuffer();
2587 
2588     // Drop Track
2589     ControlObject::set(ConfigKey(m_sGroup1, "volume"), 1.0);
2590     ProcessBuffer();
2591     ProcessBuffer();
2592 
2593     ASSERT_FALSE(isSoftMaster(m_sGroup1));
2594     ASSERT_FALSE(isSoftMaster(m_sGroup2));
2595     ASSERT_TRUE(isSoftMaster(m_sInternalClockGroup));
2596 }
2597