diff --git a/src/baseplayer.cpp b/src/baseplayer.cpp index e7302da543c..93e3725164e 100644 --- a/src/baseplayer.cpp +++ b/src/baseplayer.cpp @@ -9,6 +9,6 @@ BasePlayer::~BasePlayer() { } -const QString BasePlayer::getGroup() { +const QString BasePlayer::getGroup() const { return m_group; } diff --git a/src/baseplayer.h b/src/baseplayer.h index dfbf868c15f..f736b61c741 100644 --- a/src/baseplayer.h +++ b/src/baseplayer.h @@ -10,7 +10,7 @@ class BasePlayer : public QObject { BasePlayer(QObject* pParent, QString group); virtual ~BasePlayer(); - const QString getGroup(); + const QString getGroup() const; private: const QString m_group; diff --git a/src/dlgprefcontrols.cpp b/src/dlgprefcontrols.cpp index a157b4973a2..09361fadd9e 100644 --- a/src/dlgprefcontrols.cpp +++ b/src/dlgprefcontrols.cpp @@ -195,6 +195,11 @@ DlgPrefControls::DlgPrefControls(QWidget * parent, MixxxApp * mixxx, ComboBoxAutoDjRequeue->setCurrentIndex(m_pConfig->getValueString(ConfigKey("[Auto DJ]", "Requeue")).toInt()); connect(ComboBoxAutoDjRequeue, SIGNAL(activated(int)), this, SLOT(slotSetAutoDjRequeue(int))); + // Ordering of decks, if configurable + int deck_count = static_cast(m_pNumDecks->get()); + updateDeckOrderCombo(deck_count); + connect(ComboBoxDeckOrder, SIGNAL(activated(int)), this, SLOT(slotSetDeckOrder(int))); + #ifdef __AUTODJCRATES__ // The minimum available for randomly-selected tracks @@ -448,6 +453,14 @@ void DlgPrefControls::slotSetAutoDjRequeue(int) m_pConfig->set(ConfigKey("[Auto DJ]", "Requeue"), ConfigValue(ComboBoxAutoDjRequeue->currentIndex())); } +void DlgPrefControls::slotSetDeckOrder(int) +{ + QString deckorder = ComboBoxDeckOrder->currentText(); + m_pConfig->set(ConfigKey("[Controls]", "DeckOrder"), ConfigValue(deckorder)); + updateDeckOrderCombo(static_cast(m_pNumDecks->get())); + m_pPlayerManager->setDeckOrder(deckorder); +} + void DlgPrefControls::slotSetAutoDjMinimumAvailable(int a_iValue) { #ifdef __AUTODJCRATES__ QString str; @@ -497,6 +510,8 @@ void DlgPrefControls::slotSetSkin(int) checkSkinResolution(ComboBoxSkinconf->currentText()) ? warningLabel->hide() : warningLabel->show(); slotUpdateSchemes(); + const int deck_count = static_cast(m_pNumDecks->get()); + updateDeckOrderCombo(deck_count); } void DlgPrefControls::slotSetPositionDisplay(int) @@ -596,6 +611,7 @@ void DlgPrefControls::slotApply() else m_pConfig->set(ConfigKey("[Controls]","RateDir"), ConfigValue(1)); + slotSetDeckOrder(ComboBoxDeckOrder->currentIndex()); } void DlgPrefControls::slotSetFrameRate(int frameRate) { @@ -759,6 +775,7 @@ void DlgPrefControls::slotNumDecksChanged(double new_count) { m_iNumConfiguredDecks = numdecks; slotSetRateDir(m_pConfig->getValueString(ConfigKey("[Controls]","RateDir")).toInt()); slotSetRateRange(m_pConfig->getValueString(ConfigKey("[Controls]","RateRange")).toInt()); + updateDeckOrderCombo(numdecks); } void DlgPrefControls::slotNumSamplersChanged(double new_count) { @@ -783,3 +800,46 @@ void DlgPrefControls::slotNumSamplersChanged(double new_count) { slotSetRateDir(m_pConfig->getValueString(ConfigKey("[Controls]","RateDir")).toInt()); slotSetRateRange(m_pConfig->getValueString(ConfigKey("[Controls]","RateRange")).toInt()); } + +void DlgPrefControls::updateDeckOrderCombo(int deck_count) { + // We always try to find the configured order because the skin deckcount value isn't set + // at construction time. We'll receive a signal when that value changes, this function + // will get called, and then we'll set the proper ordering. Since we update the config + // every time they change the value, this shouldn't cause weird overwrites. + + // Temporarily, all 4-deck setups will use the same order + QString config_order; + if (deck_count == 4) { + config_order = "CABD"; + } else { + config_order = PlayerManager::getDefaultOrder(deck_count).label; + } + + // Eventually, load from config: + //QString config_order = m_pConfig->getValueString(ConfigKey("[Controls]", "DeckOrder")); + //textDeckOrder->setVisible(deck_count != 0); + //ComboBoxDeckOrder->setVisible(deck_count != 0); + ComboBoxDeckOrder->clear(); + if (deck_count == 0) { + return; + } + + int deckorder_index = -1; + int i = 0; + foreach(const PlayerManager::DeckOrderingManager::deck_order_t& order, + PlayerManager::getDeckOrderings(deck_count)) { + ComboBoxDeckOrder->addItem(order.label); + if (order.label == config_order) { + deckorder_index = i; + } + ++i; + } + if (deckorder_index >= 0) { + ComboBoxDeckOrder->setCurrentIndex(deckorder_index); + if (ComboBoxDeckOrder->currentText() != config_order) { + m_pConfig->set(ConfigKey("[Controls]", "DeckOrder"), + ConfigValue(ComboBoxDeckOrder->currentText())); + } + m_pPlayerManager->setDeckOrder(config_order); + } +} diff --git a/src/dlgprefcontrols.h b/src/dlgprefcontrols.h index 340ed7679a3..a4ed028c3fd 100644 --- a/src/dlgprefcontrols.h +++ b/src/dlgprefcontrols.h @@ -67,11 +67,11 @@ class DlgPrefControls : public DlgPreferencePage, public Ui::DlgPrefControlsDlg void slotSetAutoDjMinimumAvailable(int); void slotSetAutoDjUseIgnoreTime(int); void slotSetAutoDjIgnoreTime(const QTime &a_rTime); + void slotSetDeckOrder(int); void slotSetRateRamp(bool); void slotSetRateRampSensitivity(int); void slotSetLocale(int); - void slotSetFrameRate(int frameRate); void slotSetWaveformType(int index); void slotSetWaveformOverviewType(int index); @@ -84,6 +84,7 @@ class DlgPrefControls : public DlgPreferencePage, public Ui::DlgPrefControlsDlg void slotSetNormalizeOverview( bool normalize); void slotWaveformMeasured(float frameRate, int rtErrorCnt); + private slots: void slotNumDecksChanged(double); void slotNumSamplersChanged(double); @@ -91,6 +92,7 @@ class DlgPrefControls : public DlgPreferencePage, public Ui::DlgPrefControlsDlg void initWaveformControl(); void notifyRebootNecessary(); bool checkSkinResolution(QString skin); + void updateDeckOrderCombo(int deck_count); ConfigObject* m_pConfig; ControlObject* m_pControlPositionDisplay; diff --git a/src/dlgprefcontrolsdlg.ui b/src/dlgprefcontrolsdlg.ui index ce71b3c2148..a6a763a9f84 100644 --- a/src/dlgprefcontrolsdlg.ui +++ b/src/dlgprefcontrolsdlg.ui @@ -80,7 +80,7 @@ - + true @@ -99,7 +99,7 @@ - + @@ -112,7 +112,7 @@ - + true @@ -131,7 +131,7 @@ - + @@ -144,7 +144,7 @@ - + true @@ -163,7 +163,7 @@ - + @@ -176,7 +176,7 @@ - + true @@ -195,7 +195,7 @@ - + @@ -208,7 +208,23 @@ - + + + + + + + Deck track load order + + + ComboBoxDeckOrder + + + false + + + + Track load behavior @@ -218,10 +234,10 @@ - + - + Cue behavior @@ -231,10 +247,10 @@ - + - + Auto Recall Cue @@ -244,10 +260,10 @@ - + - + Re-queue tracks in Auto DJ @@ -257,10 +273,10 @@ - + - + Locale @@ -270,7 +286,7 @@ - + @@ -283,14 +299,14 @@ - + Auto DJ: Minimum available tracks [%] - + This percentage of tracks are always available for selecting, regardless of when they were last played. @@ -303,7 +319,7 @@ - + Uncheck, to ignore all played tracks. @@ -313,7 +329,7 @@ - + Duration after which a track is eligible for selection by Auto DJ again diff --git a/src/playermanager.cpp b/src/playermanager.cpp index ba7651c586d..3dc5b401db5 100644 --- a/src/playermanager.cpp +++ b/src/playermanager.cpp @@ -19,6 +19,11 @@ #include "util/stat.h" #include "engine/enginedeck.h" +// static +PlayerManager::DeckOrderingManager PlayerManager::s_deckOrderingManager = + PlayerManager::DeckOrderingManager(); +PlayerManager::DeckOrderingManager::deck_order_t PlayerManager::s_currentDeckOrder; + PlayerManager::PlayerManager(ConfigObject* pConfig, SoundManager* pSoundManager, EngineMaster* pEngine) : @@ -181,6 +186,36 @@ void PlayerManager::slotNumDecksControlChanged(double v) { } } +void PlayerManager::setDeckOrder(QString deckorder) { + int num_decks = m_pCONumDecks->get(); + bool found_valid = false; + foreach(const PlayerManager::DeckOrderingManager::deck_order_t& order, + PlayerManager::getDeckOrderings(num_decks)) { + if (order.label == deckorder) { + found_valid = true; + s_currentDeckOrder = order; + } + } + if (!found_valid) { + s_currentDeckOrder = s_deckOrderingManager.getDefaultOrder(num_decks); + } + reorientDecks(); +} + +void PlayerManager::reorientDecks() { + int total_decks = static_cast(m_pCONumDecks->get()); + for (int i = 1; i < total_decks + 1; ++i) { + ControlObject* orientation = + ControlObject::getControl( + ConfigKey(QString("[Channel%1]").arg(i), "orientation")); + if (i > total_decks / 2) { + orientation->set(EngineChannel::RIGHT); + } else { + orientation->set(EngineChannel::LEFT); + } + } +} + void PlayerManager::slotNumSamplersControlChanged(double v) { QMutexLocker locker(&m_mutex); int num = (int)v; @@ -372,9 +407,9 @@ void PlayerManager::slotLoadToSampler(QString location, int sampler) { void PlayerManager::slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack) { QMutexLocker locker(&m_mutex); - QList::iterator it = m_decks.begin(); - while (it != m_decks.end()) { - Deck* pDeck = *it; + + foreach(const int& i, s_currentDeckOrder.load_order) { + Deck* pDeck = m_decks.at(i); ControlObject* playControl = ControlObject::getControl(ConfigKey(pDeck->getGroup(), "play")); if (playControl && playControl->get() != 1.) { @@ -382,7 +417,6 @@ void PlayerManager::slotLoadTrackIntoNextAvailableDeck(TrackPointer pTrack) { pDeck->slotLoadTrack(pTrack, false); return; } - it++; } } diff --git a/src/playermanager.h b/src/playermanager.h index 7fa44d3955d..fc1de3ac0fb 100644 --- a/src/playermanager.h +++ b/src/playermanager.h @@ -85,6 +85,88 @@ class PlayerManager : public QObject { // Used to determine if the user has configured an input for the given vinyl deck. bool hasVinylInput(int inputnum) const; + class DeckOrderingManager { + public: + struct deck_order_t { + deck_order_t() { } + deck_order_t(QString l, QList o) : label(l), load_order(o) { } + + QString label; + QList load_order; + }; + + typedef QHash > orders_hash_t; + + DeckOrderingManager() { + // Known orderings for 4-deck controllers. + addOrdering(4, "ABCD"); + addOrdering(4, "CABD"); + addOrdering(4, "ACDB"); + } + + bool addOrdering(int deck_count, QString order) { + deck_order_t new_order(order, makeLoadOrder(order)); + // ordering will be empty on error + if (new_order.load_order.empty()) { + return false; + } + m_hOrdersHash[deck_count].push_back(new_order); + return true; + } + + // Just take the first order in the list is default. Make sure it's the natural ordering + // "ABCD..." + deck_order_t getDefaultOrder(int deck_count) { + // Don't access the hash directly in case we need to generate a default order. + return getDeckOrderings(deck_count).at(0); + } + + const QList getDeckOrderings(int deck_count) { + orders_hash_t::const_iterator it = m_hOrdersHash.find(deck_count); + if (it == m_hOrdersHash.end()) { + m_hOrdersHash[deck_count].push_back(makeDefaultOrder(deck_count)); + } + return m_hOrdersHash[deck_count]; + } + + private: + deck_order_t makeDefaultOrder(int deck_count) const { + QString str_order; + QList int_order; + for (int i = 0; i < deck_count; ++i) { + str_order += 'A' + i; + int_order.push_back(i); + } + return deck_order_t(str_order, int_order); + } + + // Constructs a list of integers for load-order based on the string. + // If the string has errors, then we return an empty list. + QList makeLoadOrder(QString str_order) { + QList int_order; + for (int i = 0; i < str_order.length(); ++i) { + int pos = str_order.indexOf('A' + i); + if (pos == -1) { + return QList(); + } + int_order.push_back(pos); + } + return int_order; + } + + orders_hash_t m_hOrdersHash; + }; + + static const QList getDeckOrderings(int deck_count) { + return s_deckOrderingManager.getDeckOrderings(deck_count); + } + + static const DeckOrderingManager::deck_order_t getDefaultOrder(int deck_count) { + return s_deckOrderingManager.getDefaultOrder(deck_count); + } + + void setDeckOrder(QString order); + public slots: // Slots for loading tracks into a Player, which is either a Sampler or a Deck void slotLoadTrackToPlayer(TrackPointer pTrack, QString group, bool play = false); @@ -120,6 +202,9 @@ class PlayerManager : public QObject { // Must hold m_mutex before calling this method. Internal method that // creates a new preview deck. void addPreviewDeckInner(); + // When the skin changes, we need to change the orientations of the decks + // because deck B might have moved from the right to the left. + void reorientDecks(); // Used to protect access to PlayerManager state across threads. mutable QMutex m_mutex; @@ -136,6 +221,9 @@ class PlayerManager : public QObject { QList m_samplers; QList m_preview_decks; QMap m_players; + + static DeckOrderingManager s_deckOrderingManager; + static DeckOrderingManager::deck_order_t s_currentDeckOrder; }; #endif // PLAYERMANAGER_H