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