diff --git a/res/qml/Mixxx/Controls/WaveformDisplay.qml b/res/qml/Mixxx/Controls/WaveformDisplay.qml new file mode 100644 index 00000000000..3ed11593cad --- /dev/null +++ b/res/qml/Mixxx/Controls/WaveformDisplay.qml @@ -0,0 +1,7 @@ +import Mixxx 1.0 as Mixxx + +Mixxx.WaveformDisplay { + id: root + + player: Mixxx.PlayerManager.getPlayer(root.group) +} diff --git a/src/main.cpp b/src/main.cpp index 60cb824a7ef..9041dbf0f96 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "config.h" @@ -16,7 +17,11 @@ #include "errordialoghandler.h" #include "mixxxapplication.h" #ifdef MIXXX_USE_QML +#include "mixer/playermanager.h" #include "qml/qmlapplication.h" +#include "waveform/guitick.h" +#include "waveform/visualsmanager.h" +#include "waveform/waveformwidgetfactory.h" #endif #include "mixxxmainwindow.h" #if defined(__WINDOWS__) @@ -57,8 +62,30 @@ int runMixxx(MixxxApplication* pApp, const CmdlineArgs& args) { int exitCode; #ifdef MIXXX_USE_QML if (args.isQml()) { - mixxx::qml::QmlApplication qmlApplication(pApp, pCoreServices); - exitCode = pApp->exec(); + auto pTick = std::make_unique(); + auto pVisuals = std::make_unique(); + WaveformWidgetFactory::createInstance(); // takes a long time + WaveformWidgetFactory::instance()->setConfig(pCoreServices->getSettings()); + WaveformWidgetFactory::instance()->startVSync(pTick.get(), pVisuals.get()); + { + mixxx::qml::QmlApplication qmlApplication(pApp, pCoreServices); + const QStringList visualGroups = + pCoreServices->getPlayerManager()->getVisualPlayerGroups(); + for (const QString& group : visualGroups) { + pVisuals->addDeck(group); + } + pCoreServices->getPlayerManager()->connect(pCoreServices->getPlayerManager().get(), + &PlayerManager::numberOfDecksChanged, + &qmlApplication, + [&pVisuals](int decks) { + for (int i = 0; i < decks; ++i) { + QString group = PlayerManager::groupForDeck(i); + pVisuals->addDeckIfNotExist(group); + } + }); + exitCode = pApp->exec(); + } + WaveformWidgetFactory::destroy(); } else #endif { diff --git a/src/qml/qmlplayermanagerproxy.cpp b/src/qml/qmlplayermanagerproxy.cpp index d8c3fc4930e..2544b34f3ad 100644 --- a/src/qml/qmlplayermanagerproxy.cpp +++ b/src/qml/qmlplayermanagerproxy.cpp @@ -14,7 +14,7 @@ QmlPlayerManagerProxy::QmlPlayerManagerProxy( : QObject(parent), m_pPlayerManager(pPlayerManager) { } -QObject* QmlPlayerManagerProxy::getPlayer(const QString& group) { +QmlPlayerProxy* QmlPlayerManagerProxy::getPlayer(const QString& group) { BaseTrackPlayer* pPlayer = m_pPlayerManager->getPlayer(group); if (!pPlayer) { qWarning() << "PlayerManagerProxy failed to find player for group" << group; diff --git a/src/qml/qmlplayermanagerproxy.h b/src/qml/qmlplayermanagerproxy.h index e0226da802f..004123e0bd0 100644 --- a/src/qml/qmlplayermanagerproxy.h +++ b/src/qml/qmlplayermanagerproxy.h @@ -18,7 +18,7 @@ class QmlPlayerManagerProxy : public QObject { std::shared_ptr pPlayerManager, QObject* parent = nullptr); - Q_INVOKABLE QObject* getPlayer(const QString& deck); + Q_INVOKABLE QmlPlayerProxy* getPlayer(const QString& deck); Q_INVOKABLE void loadLocationIntoNextAvailableDeck(const QString& location, bool play = false); Q_INVOKABLE void loadLocationUrlIntoNextAvailableDeck( const QUrl& locationUrl, bool play = false); diff --git a/src/waveform/isynctimeprovider.h b/src/waveform/isynctimeprovider.h new file mode 100644 index 00000000000..9432249702b --- /dev/null +++ b/src/waveform/isynctimeprovider.h @@ -0,0 +1,9 @@ +#pragma once + +#include "util/performancetimer.h" + +class ISyncTimeProvider { + public: + virtual int fromTimerToNextSyncMicros(const PerformanceTimer& timer) = 0; + virtual int getSyncIntervalTimeMicros() const = 0; +}; diff --git a/src/waveform/visualplayposition.cpp b/src/waveform/visualplayposition.cpp index bb7cfc9ea93..d9d96a84092 100644 --- a/src/waveform/visualplayposition.cpp +++ b/src/waveform/visualplayposition.cpp @@ -3,7 +3,7 @@ #include "moc_visualplayposition.cpp" #include "util/cmdlineargs.h" #include "util/math.h" -#include "waveform/vsyncthread.h" +#include "waveform/isynctimeprovider.h" //static QMap> VisualPlayPosition::m_listVisualPlayPosition; @@ -57,20 +57,10 @@ void VisualPlayPosition::set( } double VisualPlayPosition::calcOffsetAtNextVSync( - VSyncThread* pVSyncThread, const VisualPlayPositionData& data) { + ISyncTimeProvider* pSyncTimeProvider, const VisualPlayPositionData& data) { if (data.m_audioBufferMicroS != 0.0) { - int refToVSync; - int syncIntervalTimeMicros; -#ifdef MIXXX_USE_QML - if (CmdlineArgs::Instance().isQml()) { - refToVSync = 0; - syncIntervalTimeMicros = 0; - } else -#endif - { - refToVSync = pVSyncThread->fromTimerToNextSyncMicros(data.m_referenceTime); - syncIntervalTimeMicros = pVSyncThread->getSyncIntervalTimeMicros(); - } + int refToVSync = pSyncTimeProvider->fromTimerToNextSyncMicros(data.m_referenceTime); + int syncIntervalTimeMicros = pSyncTimeProvider->getSyncIntervalTimeMicros(); // The positive offset is limited to the audio buffer + 2 x waveform sync interval // This should be sufficient to compensate jitter, but does not continue // in case of underflows. @@ -161,22 +151,23 @@ double VisualPlayPosition::determinePlayPosInLoopBoundries( return interpolatedPlayPos; } -double VisualPlayPosition::getAtNextVSync(VSyncThread* pVSyncThread) { +double VisualPlayPosition::getAtNextVSync(ISyncTimeProvider* pSyncTimeProvider) { if (m_valid) { const VisualPlayPositionData data = m_data.getValue(); - const double offset = calcOffsetAtNextVSync(pVSyncThread, data); + const double offset = calcOffsetAtNextVSync(pSyncTimeProvider, data); return determinePlayPosInLoopBoundries(data, offset); } return -1; } -void VisualPlayPosition::getPlaySlipAtNextVSync(VSyncThread* pVSyncThread, +void VisualPlayPosition::getPlaySlipAtNextVSync( + ISyncTimeProvider* pSyncTimeProvider, double* pPlayPosition, double* pSlipPosition) { if (m_valid) { const VisualPlayPositionData data = m_data.getValue(); - const double offset = calcOffsetAtNextVSync(pVSyncThread, data); + const double offset = calcOffsetAtNextVSync(pSyncTimeProvider, data); double interpolatedPlayPos = determinePlayPosInLoopBoundries(data, offset); *pPlayPosition = interpolatedPlayPos; diff --git a/src/waveform/visualplayposition.h b/src/waveform/visualplayposition.h index 71532d07d2f..f0d9329d494 100644 --- a/src/waveform/visualplayposition.h +++ b/src/waveform/visualplayposition.h @@ -9,7 +9,7 @@ #include "util/performancetimer.h" class ControlProxy; -class VSyncThread; +class ISyncTimeProvider; // This class is for synchronizing the sound device DAC time with the waveforms, displayed on the // graphic device, using the CPU time @@ -67,8 +67,8 @@ class VisualPlayPosition : public QObject { double tempoTrackSeconds, double audioBufferMicroS); - double getAtNextVSync(VSyncThread* pVSyncThread); - void getPlaySlipAtNextVSync(VSyncThread* pVSyncThread, + double getAtNextVSync(ISyncTimeProvider* pSyncTimeProvider); + void getPlaySlipAtNextVSync(ISyncTimeProvider* pSyncTimeProvider, double* playPosition, double* slipPosition); double determinePlayPosInLoopBoundries( @@ -89,7 +89,8 @@ class VisualPlayPosition : public QObject { } private: - double calcOffsetAtNextVSync(VSyncThread* pVSyncThread, const VisualPlayPositionData& data); + double calcOffsetAtNextVSync(ISyncTimeProvider* pSyncTimeProvider, + const VisualPlayPositionData& data); ControlValueAtomic m_data; bool m_valid; QString m_key; diff --git a/src/waveform/vsyncthread.h b/src/waveform/vsyncthread.h index 48ded9f5f08..fd89dd58b30 100644 --- a/src/waveform/vsyncthread.h +++ b/src/waveform/vsyncthread.h @@ -6,10 +6,11 @@ #include #include "util/performancetimer.h" +#include "waveform/isynctimeprovider.h" class WGLWidget; -class VSyncThread : public QThread { +class VSyncThread : public QThread, public ISyncTimeProvider { Q_OBJECT public: enum VSyncMode { @@ -26,20 +27,22 @@ class VSyncThread : public QThread { VSyncThread(QObject* pParent, VSyncMode vSyncMode); ~VSyncThread(); - void run(); + void run() override; bool waitForVideoSync(WGLWidget* glw); int elapsed(); void setSyncIntervalTimeMicros(int usSyncTimer); int droppedFrames(); void setSwapWait(int sw); - int fromTimerToNextSyncMicros(const PerformanceTimer& timer); + // ISyncTimerProvider + int fromTimerToNextSyncMicros(const PerformanceTimer& timer) override; void vsyncSlotFinished(); void getAvailableVSyncTypes(QList>* list); void setupSync(WGLWidget* glw, int index); void waitUntilSwap(WGLWidget* glw); mixxx::Duration sinceLastSwap() const; - int getSyncIntervalTimeMicros() const { + // ISyncTimerProvider + int getSyncIntervalTimeMicros() const override { return m_syncIntervalTimeMicros; } void updatePLL();