From 1c2e1f8996f812cc5f572d79fdcaf2659d2521df Mon Sep 17 00:00:00 2001 From: Jan Holthuis Date: Sun, 14 Mar 2021 02:18:34 +0100 Subject: [PATCH] Use controller syncables in EngineSync [WIP] --- src/controllers/controllermanager.cpp | 15 ++++++- src/controllers/controllermanager.h | 4 +- src/coreservices.cpp | 2 +- src/engine/sync/basesyncablelistener.cpp | 46 +++++++++++++++++++ src/engine/sync/basesyncablelistener.h | 8 ++++ src/engine/sync/enginesync.cpp | 57 ++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 3 deletions(-) diff --git a/src/controllers/controllermanager.cpp b/src/controllers/controllermanager.cpp index 48472ae69f4..5027e0594ca 100644 --- a/src/controllers/controllermanager.cpp +++ b/src/controllers/controllermanager.cpp @@ -6,6 +6,8 @@ #include "controllers/controllerlearningeventfilter.h" #include "controllers/defs_controllers.h" #include "controllers/midi/portmidienumerator.h" +#include "engine/enginemaster.h" +#include "engine/sync/enginesync.h" #include "moc_controllermanager.cpp" #include "util/cmdlineargs.h" #include "util/time.h" @@ -78,9 +80,10 @@ bool controllerCompare(Controller *a,Controller *b) { return a->getName() < b->getName(); } -ControllerManager::ControllerManager(UserSettingsPointer pConfig) +ControllerManager::ControllerManager(UserSettingsPointer pConfig, EngineMaster* pMixingEngine) : QObject(), m_pConfig(pConfig), + m_pMixingEngine(pMixingEngine), // WARNING: Do not parent m_pControllerLearningEventFilter to // ControllerManager because the CM is moved to its own thread and runs // its own event loop. @@ -192,7 +195,17 @@ void ControllerManager::updateControllerList() { locker.relock(); if (newDeviceList != m_controllers) { + m_pMixingEngine->getEngineSync()->setControllerSyncables({}); m_controllers = newDeviceList; + + QList> controllerSyncables; + for (const auto* pController : qAsConst(m_controllers)) { + const std::shared_ptr pSyncable = pController->syncable(); + if (pSyncable != nullptr) { + controllerSyncables.append(pSyncable); + } + } + m_pMixingEngine->getEngineSync()->setControllerSyncables(controllerSyncables); locker.unlock(); emit devicesChanged(); } diff --git a/src/controllers/controllermanager.h b/src/controllers/controllermanager.h index 7cb776afb57..3d0b62655e7 100644 --- a/src/controllers/controllermanager.h +++ b/src/controllers/controllermanager.h @@ -12,6 +12,7 @@ // Forward declaration(s) class Controller; class ControllerLearningEventFilter; +class EngineMaster; /// Function to sort controllers by name bool controllerCompare(Controller *a, Controller *b); @@ -20,7 +21,7 @@ bool controllerCompare(Controller *a, Controller *b); class ControllerManager : public QObject { Q_OBJECT public: - ControllerManager(UserSettingsPointer pConfig); + ControllerManager(UserSettingsPointer pConfig, EngineMaster* pMixingEngine); virtual ~ControllerManager(); static const mixxx::Duration kPollInterval; @@ -73,6 +74,7 @@ class ControllerManager : public QObject { private: UserSettingsPointer m_pConfig; + EngineMaster* m_pMixingEngine; ControllerLearningEventFilter* m_pControllerLearningEventFilter; QTimer m_pollTimer; mutable QMutex m_mutex; diff --git a/src/coreservices.cpp b/src/coreservices.cpp index 5eae68f0278..f1f5a074b2d 100644 --- a/src/coreservices.cpp +++ b/src/coreservices.cpp @@ -309,7 +309,7 @@ void CoreServices::initialize(QApplication* pApp) { // but do not set up controllers until the end of the application startup // (long) qDebug() << "Creating ControllerManager"; - m_pControllerManager = std::make_shared(pConfig); + m_pControllerManager = std::make_shared(pConfig, m_pEngine.get()); // Inhibit the screensaver if the option is set. (Do it before creating the preferences dialog) int inhibit = pConfig->getValue(ConfigKey("[Config]", "InhibitScreensaver"), -1); diff --git a/src/engine/sync/basesyncablelistener.cpp b/src/engine/sync/basesyncablelistener.cpp index e2c309c6bc4..bde0ced5b3a 100644 --- a/src/engine/sync/basesyncablelistener.cpp +++ b/src/engine/sync/basesyncablelistener.cpp @@ -60,6 +60,11 @@ bool BaseSyncableListener::syncDeckExists() const { return true; } } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (pSyncable->isSynchronized() && pSyncable->getBaseBpm() > 0) { + return true; + } + } return false; } @@ -96,6 +101,13 @@ void BaseSyncableListener::setMasterBpm(Syncable* pSource, double bpm) { } pSyncable->setMasterBpm(bpm); } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (pSyncable.get() == pSource || + !pSyncable->isSynchronized()) { + continue; + } + pSyncable->setMasterBpm(bpm); + } } void BaseSyncableListener::setMasterInstantaneousBpm(Syncable* pSource, double bpm) { @@ -109,6 +121,13 @@ void BaseSyncableListener::setMasterInstantaneousBpm(Syncable* pSource, double b } pSyncable->setInstantaneousBpm(bpm); } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (pSyncable.get() == pSource || + !pSyncable->isSynchronized()) { + continue; + } + pSyncable->setInstantaneousBpm(bpm); + } } void BaseSyncableListener::setMasterBeatDistance(Syncable* pSource, double beat_distance) { @@ -122,6 +141,13 @@ void BaseSyncableListener::setMasterBeatDistance(Syncable* pSource, double beat_ } pSyncable->setMasterBeatDistance(beat_distance); } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (pSyncable.get() == pSource || + !pSyncable->isSynchronized()) { + continue; + } + pSyncable->setMasterBeatDistance(beat_distance); + } } void BaseSyncableListener::setMasterParams(Syncable* pSource, double beat_distance, @@ -137,6 +163,13 @@ void BaseSyncableListener::setMasterParams(Syncable* pSource, double beat_distan } pSyncable->setMasterParams(beat_distance, base_bpm, bpm); } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (pSyncable.get() == pSource || + !pSyncable->isSynchronized()) { + continue; + } + pSyncable->setMasterParams(beat_distance, base_bpm, bpm); + } } void BaseSyncableListener::checkUniquePlayingSyncable() { @@ -155,6 +188,19 @@ void BaseSyncableListener::checkUniquePlayingSyncable() { ++playing_sync_decks; } } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (!pSyncable->isSynchronized()) { + continue; + } + + if (pSyncable->isPlaying()) { + if (playing_sync_decks > 0) { + return; + } + unique_syncable = pSyncable.get(); + ++playing_sync_decks; + } + } if (playing_sync_decks == 1) { unique_syncable->notifyOnlyPlayingSyncable(); } diff --git a/src/engine/sync/basesyncablelistener.h b/src/engine/sync/basesyncablelistener.h index ca3376ca83f..2e371658839 100644 --- a/src/engine/sync/basesyncablelistener.h +++ b/src/engine/sync/basesyncablelistener.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "engine/sync/syncable.h" #include "preferences/usersettings.h" @@ -21,6 +24,10 @@ class BaseSyncableListener : public SyncableListener { void onCallbackStart(int sampleRate, int bufferSize); void onCallbackEnd(int sampleRate, int bufferSize); + void setControllerSyncables(const QList>& syncables) { + m_controllerSyncables = syncables; + } + // Only for testing. Do not use. Syncable* getSyncableForGroup(const QString& group); Syncable* getMasterSyncable() override { @@ -69,4 +76,5 @@ class BaseSyncableListener : public SyncableListener { // The list of all Syncables registered with BaseSyncableListener via // addSyncableDeck. QList m_syncables; + QList> m_controllerSyncables; }; diff --git a/src/engine/sync/enginesync.cpp b/src/engine/sync/enginesync.cpp index bf27fbd1a72..2711a9c0811 100644 --- a/src/engine/sync/enginesync.cpp +++ b/src/engine/sync/enginesync.cpp @@ -56,6 +56,29 @@ Syncable* EngineSync::pickMaster(Syncable* enabling_syncable) { stopped_deck_count++; } } + for (const auto& pSyncable : qAsConst(m_controllerSyncables)) { + if (pSyncable->getBaseBpm() <= 0.0) { + continue; + } + + if (pSyncable.get() != enabling_syncable) { + if (!pSyncable->isSynchronized()) { + continue; + } + } + + if (pSyncable->isPlaying()) { + if (playing_deck_count == 0) { + first_playing_deck = pSyncable.get(); + } + playing_deck_count++; + } else { + if (stopped_deck_count == 0) { + first_stopped_deck = pSyncable.get(); + } + stopped_deck_count++; + } + } if (playing_deck_count == 1) { return first_playing_deck; @@ -147,6 +170,28 @@ Syncable* EngineSync::findBpmMatchTarget(Syncable* requester) { } } + for (const auto& pOtherSyncable : qAsConst(m_controllerSyncables)) { + if (pOtherSyncable.get() == requester) { + continue; + } + if (pOtherSyncable->getBaseBpm() == 0.0) { + continue; + } + + // If the other deck is playing we stop looking immediately. Otherwise continue looking + // for a playing deck with bpm > 0.0. + if (pOtherSyncable->isPlaying()) { + return pOtherSyncable.get(); + } + + // The target is not playing. If this is the first one we have seen, + // record it. If we never find a playing target, we'll return + // this one as a fallback. + if (!pStoppedTarget && !requester->isPlaying()) { + pStoppedTarget = pOtherSyncable.get(); + } + } + return pStoppedTarget; } @@ -470,5 +515,17 @@ bool EngineSync::otherSyncedPlaying(const QString& group) { othersInSync = true; } } + for (const auto& theSyncable : qAsConst(m_controllerSyncables)) { + bool isSynchonized = theSyncable->isSynchronized(); + if (theSyncable->getGroup() == group) { + if (!isSynchonized) { + return false; + } + continue; + } + if (theSyncable->isPlaying() && isSynchonized) { + othersInSync = true; + } + } return othersInSync; }