diff --git a/build/depends.py b/build/depends.py index 2f29cff6e08..164edd16bdd 100644 --- a/build/depends.py +++ b/build/depends.py @@ -422,6 +422,7 @@ def sources(self, build): "configobject.cpp", "control/control.cpp", "control/controlbehavior.cpp", + "controlobjectslave.cpp", "controlobjectthread.cpp", "controlobjectthreadwidget.cpp", "controlobjectthreadmain.cpp", diff --git a/src/controlobjectslave.cpp b/src/controlobjectslave.cpp new file mode 100644 index 00000000000..5b701f672f2 --- /dev/null +++ b/src/controlobjectslave.cpp @@ -0,0 +1,90 @@ +#include +#include + +#include "controlobjectslave.h" +#include "control/control.h" + +ControlObjectSlave::ControlObjectSlave(QObject* pParent) + : QObject(pParent), + m_pControl(NULL) { +} + +ControlObjectSlave::ControlObjectSlave(const QString& g, const QString& i, QObject* pParent) + : QObject(pParent) { + initialize(ConfigKey(g, i)); +} + +ControlObjectSlave::ControlObjectSlave(const char* g, const char* i, QObject* pParent) + : QObject(pParent) { + initialize(ConfigKey(g, i)); +} + +ControlObjectSlave::ControlObjectSlave(const ConfigKey& key, QObject* pParent) + : QObject(pParent) { + initialize(key); +} + +void ControlObjectSlave::initialize(const ConfigKey& key) { + m_pControl = ControlDoublePrivate::getControl(key); +} + +ControlObjectSlave::~ControlObjectSlave() { +} + +bool ControlObjectSlave::connectValueChanged(const QObject* receiver, + const char* method, Qt::ConnectionType type) { + bool ret = false; + if (m_pControl) { + ret = connect((QObject*)this, SIGNAL(valueChanged(double)), + receiver, method, type); + if (ret) { + // connect to ControlObjectPrivate only if required + ret = connect(m_pControl.data(), SIGNAL(valueChanged(double, QObject*)), + this, SLOT(slotValueChanged(double, QObject*)), + Qt::DirectConnection); + } + } + return ret; +} + +bool ControlObjectSlave::connectValueChanged( + const char* method, Qt::ConnectionType type) { + return connectValueChanged(parent(), method, type); +} + + +double ControlObjectSlave::get() { + return m_pControl ? m_pControl->get() : 0.0; +} + +void ControlObjectSlave::slotSet(double v) { + set(v); +} + +void ControlObjectSlave::set(double v) { + if (m_pControl) { + m_pControl->set(v, this); + } +} + +void ControlObjectSlave::reset() { + if (m_pControl) { + // NOTE(rryan): This is important. The originator of this action does + // not know the resulting value so it makes sense that we should emit a + // general valueChanged() signal even though the change originated from + // us. For this reason, we provide NULL here so that the change is + // broadcast as valueChanged() and not valueChangedByThis(). + m_pControl->reset(); + } +} + +void ControlObjectSlave::emitValueChanged() { + emit(valueChanged(get())); +} + +void ControlObjectSlave::slotValueChanged(double v, QObject* pSetter) { + if (pSetter != this) { + // This is base implementation of this function without scaling + emit(valueChanged(v)); + } +} diff --git a/src/controlobjectslave.h b/src/controlobjectslave.h new file mode 100644 index 00000000000..06eaef93ff5 --- /dev/null +++ b/src/controlobjectslave.h @@ -0,0 +1,69 @@ +#ifndef CONTROLOBJECTSLAVE_H +#define CONTROLOBJECTSLAVE_H + +#include +#include +#include +#include +#include +#include + +#include "configobject.h" + +class ControlDoublePrivate; + +// This class is the successor of ControlObjectThread it should be used for new +// code. I it is better named and may save some CPU time because it is connected +// only on demand. There are many ControlObjectThread instances where the changed +// signal is not needed. This change will save the set() caller for doing +// unnecessary checks for possible connections. +class ControlObjectSlave : public QObject { + Q_OBJECT + public: + ControlObjectSlave(QObject* pParent = NULL); + ControlObjectSlave(const QString& g, const QString& i, QObject* pParent = NULL); + ControlObjectSlave(const char* g, const char* i, QObject* pParent = NULL); + ControlObjectSlave(const ConfigKey& key, QObject* pParent = NULL); + virtual ~ControlObjectSlave(); + + void initialize(const ConfigKey& key); + + bool connectValueChanged(const QObject* receiver, + const char* method, Qt::ConnectionType type = Qt::AutoConnection); + bool connectValueChanged( + const char* method, Qt::ConnectionType type = Qt::AutoConnection ); + + + // Called from update(); + void emitValueChanged(); + + inline bool valid() const { return m_pControl != NULL; } + + // Returns the value of the object. Thread safe, non-blocking. + virtual double get(); + + public slots: + // Set the control to a new value. Non-blocking. + virtual void slotSet(double v); + // Sets the control value to v. Thread safe, non-blocking. + virtual void set(double v); + // Resets the control to its default value. Thread safe, non-blocking. + virtual void reset(); + + signals: + // This signal must not connected by connect() + // Use connectValueChanged() instead, It will connect + // to the base ControlDoublePrivate as well + void valueChanged(double); + + protected slots: + // Receives the value from the master control and re-emits either + // valueChanged(double) or valueChangedByThis(double) based on pSetter. + virtual void slotValueChanged(double v, QObject* pSetter); + + protected: + // Pointer to connected control. + QSharedPointer m_pControl; +}; + +#endif // CONTROLOBJECTSLAVE_H diff --git a/src/controlobjectthread.cpp b/src/controlobjectthread.cpp index fc9d2152f3b..1c957cb1c1a 100644 --- a/src/controlobjectthread.cpp +++ b/src/controlobjectthread.cpp @@ -49,6 +49,17 @@ void ControlObjectThread::initialize(const ConfigKey& key) { ControlObjectThread::~ControlObjectThread() { } +bool ControlObjectThread::connectValueChanged(const QObject* receiver, + const char* method, Qt::ConnectionType type) { + return connect((QObject*)this, SIGNAL(valueChanged(double)), receiver, method, type); +} + +bool ControlObjectThread::connectValueChanged( + const char* method, Qt::ConnectionType type) { + return connect((QObject*)this, SIGNAL(valueChanged(double)), parent(), method, type); +} + + double ControlObjectThread::get() { return m_pControl ? m_pControl->get() : 0.0; } diff --git a/src/controlobjectthread.h b/src/controlobjectthread.h index 779619cd710..848eff65f09 100644 --- a/src/controlobjectthread.h +++ b/src/controlobjectthread.h @@ -38,6 +38,12 @@ class ControlObjectThread : public QObject { void initialize(const ConfigKey& key); + bool connectValueChanged(const QObject* receiver, + const char* method, Qt::ConnectionType type = Qt::AutoConnection); + bool connectValueChanged( + const char* method, Qt::ConnectionType type = Qt::AutoConnection ); + + /** Called from update(); */ void emitValueChanged(); diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index f1b3fd5e62c..4971aea0889 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -10,6 +10,7 @@ #include "engine/bpmcontrol.h" #include "engine/enginechannel.h" #include "engine/enginemaster.h" +#include "controlobjectslave.h" const int minBpm = 30; const int maxInterval = (int)(1000.*(60./(CSAMPLE)minBpm)); @@ -19,14 +20,9 @@ BpmControl::BpmControl(const char* _group, ConfigObject* _config) : EngineControl(_group, _config), m_tapFilter(this, filterLength, maxInterval) { - m_pPlayButton = ControlObject::getControl(_group, "play"); - m_pRateSlider = ControlObject::getControl(_group, "rate"); - connect(m_pRateSlider, SIGNAL(valueChanged(double)), - this, SLOT(slotAdjustBpm()), - Qt::DirectConnection); - connect(m_pRateSlider, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotAdjustBpm()), - Qt::DirectConnection); + m_pPlayButton = new ControlObjectSlave(_group, "play", this); + m_pRateSlider = new ControlObjectSlave(_group, "rate", this); + m_pRateSlider->connectValueChanged(SLOT(slotAdjustBpm()), Qt::DirectConnection); m_pRateRange = ControlObject::getControl(_group, "rateRange"); connect(m_pRateRange, SIGNAL(valueChanged(double)), diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index b10a8c02230..bddab81e899 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -11,6 +11,7 @@ class ControlObject; class ControlPushButton; class EngineBuffer; +class ControlObjectSlave; class BpmControl : public EngineControl { Q_OBJECT @@ -42,8 +43,8 @@ class BpmControl : public EngineControl { bool syncPhase(EngineBuffer* pOtherEngineBuffer); // ControlObjects that come from EngineBuffer - ControlObject* m_pPlayButton; - ControlObject* m_pRateSlider; + ControlObjectSlave* m_pPlayButton; + ControlObjectSlave* m_pRateSlider; ControlObject* m_pRateRange; ControlObject* m_pRateDir; diff --git a/src/engine/clockcontrol.cpp b/src/engine/clockcontrol.cpp index 2349fae033e..1965b747db3 100644 --- a/src/engine/clockcontrol.cpp +++ b/src/engine/clockcontrol.cpp @@ -4,12 +4,13 @@ #include "configobject.h" #include "cachingreader.h" #include "engine/enginecontrol.h" +#include "controlobjectslave.h" ClockControl::ClockControl(const char* pGroup, ConfigObject* pConfig) : EngineControl(pGroup, pConfig) { m_pCOBeatActive = new ControlObject(ConfigKey(pGroup, "beat_active")); m_pCOBeatActive->set(0.0f); - m_pCOSampleRate = ControlObject::getControl(ConfigKey("[Master]","samplerate")); + m_pCOSampleRate = new ControlObjectSlave("[Master]","samplerate"); } ClockControl::~ClockControl() { diff --git a/src/engine/clockcontrol.h b/src/engine/clockcontrol.h index e6ee1d1c975..aa7441194ab 100644 --- a/src/engine/clockcontrol.h +++ b/src/engine/clockcontrol.h @@ -7,6 +7,8 @@ #include "trackinfoobject.h" #include "track/beats.h" +class ControlObjectSlave; + class ClockControl: public EngineControl { Q_OBJECT public: @@ -25,7 +27,7 @@ class ClockControl: public EngineControl { private: ControlObject* m_pCOBeatActive; - ControlObject* m_pCOSampleRate; + ControlObjectSlave* m_pCOSampleRate; TrackPointer m_pTrack; BeatsPointer m_pBeats; }; diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 4481eb17950..691cdb24d5b 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -40,6 +40,7 @@ #include "engine/cuecontrol.h" #include "engine/clockcontrol.h" #include "util/timer.h" +#include "controlobjectslave.h" #ifdef __VINYLCONTROL__ #include "engine/vinylcontrolcontrol.h" @@ -176,7 +177,7 @@ EngineBuffer::EngineBuffer(const char * _group, ConfigObject * _con m_pRepeat->setButtonMode(ControlPushButton::TOGGLE); // Sample rate - m_pSampleRate = ControlObject::getControl(ConfigKey("[Master]","samplerate")); + m_pSampleRate = new ControlObjectSlave("[Master]", "samplerate", this); m_pTrackSamples = new ControlObject(ConfigKey(m_group, "track_samples")); m_pTrackSampleRate = new ControlObject(ConfigKey(m_group, "track_samplerate")); diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 34b9fe47f9d..8d4cb06ee07 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -41,6 +41,7 @@ class ClockControl; class CueControl; class ReadAheadManager; class ControlObject; +class ControlObjectSlave; class ControlPushButton; class ControlObjectThreadMain; class ControlBeat; @@ -235,7 +236,7 @@ class EngineBuffer : public EngineObject ControlObject* m_pMasterRate; ControlPotmeter* m_playposSlider; ControlPotmeter* m_visualPlaypos; - ControlObject* m_pSampleRate; + ControlObjectSlave* m_pSampleRate; ControlPushButton* m_pKeylock; ControlPushButton* m_pEject; diff --git a/src/engine/enginecontrol.cpp b/src/engine/enginecontrol.cpp index a95a0321d6d..5bea4a38751 100644 --- a/src/engine/enginecontrol.cpp +++ b/src/engine/enginecontrol.cpp @@ -137,4 +137,4 @@ EngineBuffer* EngineControl::pickSyncTarget() { // No playing decks have a BPM. Go with the first deck that was stopped but // had a BPM. return pFirstNonplayingDeck; -} +} \ No newline at end of file diff --git a/src/library/librarycontrol.cpp b/src/library/librarycontrol.cpp index dd7ff05a68f..de2302d2ffe 100644 --- a/src/library/librarycontrol.cpp +++ b/src/library/librarycontrol.cpp @@ -48,9 +48,9 @@ LibraryControl::LibraryControl(QObject* pParent) : QObject(pParent), m_pLibraryWidget(NULL), m_pSidebarWidget(NULL), - m_numDecks(ConfigKey("[Master]", "num_decks")), - m_numSamplers(ConfigKey("[Master]", "num_samplers")), - m_numPreviewDecks(ConfigKey("[Master]", "num_preview_decks")) { + m_numDecks("[Master]", "num_decks"), + m_numSamplers("[Master]", "num_samplers"), + m_numPreviewDecks("[Master]", "num_preview_decks") { slotNumDecksChanged(m_numDecks.get()); slotNumSamplersChanged(m_numSamplers.get());