diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp index a70675975f7f..3ffc78cf7a6b 100644 --- a/src/engine/bpmcontrol.cpp +++ b/src/engine/bpmcontrol.cpp @@ -118,7 +118,7 @@ BpmControl::BpmControl(const char* _group, this, SLOT(slotMasterBpmChanged(double)), Qt::DirectConnection); - m_pSyncMasterEnabled = new ControlPushButton(ConfigKey(_group, "sync_master")); + m_pSyncMasterEnabled = new ControlPushButton(ConfigKey(_group, "sync_master")); m_pSyncMasterEnabled->setButtonMode(ControlPushButton::TOGGLE); connect(m_pSyncMasterEnabled, SIGNAL(valueChanged(double)), this, SLOT(slotSyncMasterChanged(double)), @@ -324,44 +324,6 @@ bool BpmControl::syncTempo() { return false; } -EngineBuffer* BpmControl::pickSyncTarget() { - EngineMaster* pMaster = getEngineMaster(); - if (!pMaster) { - return NULL; - } - QString group = getGroup(); - QStringList deckGroups; - EngineBuffer* pFirstNonplayingDeck = NULL; - - for (int i = 0; i < m_pNumDecks->get(); ++i) { - // TODO(XXX) format from PlayerManager - QString deckGroup = QString("[Channel%1]").arg(i+1); - if (deckGroup == group) { - continue; - } - EngineChannel* pChannel = pMaster->getChannel(deckGroup); - // Only consider channels that have a track loaded and are in the master - // mix. - if (pChannel && pChannel->isActive() && pChannel->isMaster()) { - EngineBuffer* pBuffer = pChannel->getEngineBuffer(); - if (pBuffer && pBuffer->getBpm() > 0) { - // If the deck is playing then go with it immediately. - if (fabs(pBuffer->getRate()) > 0) { - return pBuffer; - } - // Otherwise hold out for a deck that might be playing but - // remember the first deck that matched our criteria. - if (pFirstNonplayingDeck == NULL) { - pFirstNonplayingDeck = pBuffer; - } - } - } - } - // No playing decks have a BPM. Go with the first deck that was stopped but - // had a BPM. - return pFirstNonplayingDeck; -} - void BpmControl::slotMasterBpmChanged(double syncbpm) { // Vinyl overrides if (m_pVCEnabled && m_pVCEnabled->get() > 0) { diff --git a/src/engine/bpmcontrol.h b/src/engine/bpmcontrol.h index 0293bbe625e0..b9664f7d751e 100644 --- a/src/engine/bpmcontrol.h +++ b/src/engine/bpmcontrol.h @@ -59,7 +59,6 @@ class BpmControl : public EngineControl { private: double getBeatDistance(double dThisPosition) const; - EngineBuffer* pickSyncTarget(); bool syncTempo(); bool syncPhase(); diff --git a/src/engine/enginemaster.cpp b/src/engine/enginemaster.cpp index 51544467e298..0101a8bb3f72 100644 --- a/src/engine/enginemaster.cpp +++ b/src/engine/enginemaster.cpp @@ -60,7 +60,7 @@ EngineMaster::EngineMaster(ConfigObject * _config, m_pMasterRate = new ControlPotmeter(ConfigKey(group, "rate"), -1.0, 1.0); // Master sync controller - m_pMasterSync = new EngineSync(this, _config); + m_pMasterSync = new EngineSync(_config); // TODO(owen): save / restore default bpm ControlObject::getControl(ConfigKey("[Master]","sync_bpm"))->set(124.0); ControlObject::getControl(ConfigKey("[Master]","rate"))->set(124.0); @@ -134,6 +134,7 @@ EngineMaster::~EngineMaster() delete xFaderCurve; delete xFaderMode; + delete m_pMasterSync; delete m_pMasterSampleRate; delete m_pMasterLatency; delete m_pMasterAudioBufferSize; @@ -166,16 +167,6 @@ const CSAMPLE* EngineMaster::getHeadphoneBuffer() const return m_pHead; } -EngineSync* EngineMaster::getMasterSync(void) -{ - return m_pMasterSync; -} - -void EngineMaster::setMasterSync(QString deck) -{ - m_pMasterSync->setDeckMaster(deck); -} - void EngineMaster::mixChannels(unsigned int channelBitvector, unsigned int maxChannels, CSAMPLE* pOutput, unsigned int iBufferSize, GainCalculator* pGainCalculator) { @@ -339,44 +330,19 @@ void EngineMaster::mixChannels(unsigned int channelBitvector, unsigned int maxCh } } -void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize) { - static bool haveSetName = false; - if (!haveSetName) { - QThread::currentThread()->setObjectName("Engine"); - haveSetName = true; - } - ScopedTimer t("EngineMaster::process"); - - CSAMPLE **pOutput = (CSAMPLE**)pOut; - Q_UNUSED(pOutput); - - // Prepare each channel for output - - // Bitvector of enabled channels - const unsigned int maxChannels = 32; - unsigned int masterOutput = 0; - unsigned int headphoneOutput = 0; - - // Compute headphone mix - // Head phone left/right mix - CSAMPLE cf_val = head_mix->get(); - CSAMPLE chead_gain = 0.5*(-cf_val+1.); - CSAMPLE cmaster_gain = 0.5*(cf_val+1.); - // qDebug() << "head val " << cf_val << ", head " << chead_gain - // << ", master " << cmaster_gain; - - // Increment internal buffer first in case it is the master - m_pMasterSync->incrementPseudoPosition(iBufferSize); - - // TODO(owen): MIDI goes here, probably. - - //find the Sync Master and process it first - //then process all the slaves (and skip the master) +void EngineMaster::processChannels(unsigned int* masterOutput, + unsigned int* headphoneOutput, + int iBufferSize) { + ScopedTimer timer("EngineMaster::processChannels"); - Timer timer("EngineMaster::process channels"); QList::iterator it = m_channels.begin(); QList::iterator master_it = NULL; - if (m_pMasterSync->getMaster() != NULL) { + + // Find the Sync Master and process it first then process all the slaves + // (and skip the master). + + EngineChannel* pMasterChannel = m_pMasterSync->getMaster(); + if (pMasterChannel != NULL) { for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; @@ -384,26 +350,25 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff if (!pChannel || !pChannel->isActive()) { continue; } - EngineBuffer* pBuffer = pChannel->getEngineBuffer(); - if (pBuffer == m_pMasterSync->getMaster()->getEngineBuffer()) { + + if (pMasterChannel == pChannel) { master_it = it; - //proceed with the processing as below + // Proceed with the processing as below. bool needsProcessing = false; if (pChannel->isMaster()) { - masterOutput |= (1 << channel_number); + *masterOutput |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { - headphoneOutput |= (1 << channel_number); + *headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary, which it damn well better be - Q_ASSERT(needsProcessing); if (needsProcessing) { pChannel->process(NULL, pChannelInfo->m_pBuffer, iBufferSize); } @@ -418,25 +383,26 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; + // Skip the master since we already processed it. if (it == master_it) { - // We already processed this. continue; } - if (!pChannel->isActive()) { + // Skip inactive channels. + if (!pChannel || !pChannel->isActive()) { continue; } bool needsProcessing = false; if (pChannel->isMaster()) { - masterOutput |= (1 << channel_number); + *masterOutput |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { - headphoneOutput |= (1 << channel_number); + *headphoneOutput |= (1 << channel_number); needsProcessing = true; } @@ -445,7 +411,38 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff pChannel->process(NULL, pChannelInfo->m_pBuffer, iBufferSize); } } - timer.elapsed(true); +} + +void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize) { + static bool haveSetName = false; + if (!haveSetName) { + QThread::currentThread()->setObjectName("Engine"); + haveSetName = true; + } + ScopedTimer t("EngineMaster::process"); + + // Notify EngineSync that we are starting the callback. + m_pMasterSync->onCallbackStart(iBufferSize); + + CSAMPLE **pOutput = (CSAMPLE**)pOut; + Q_UNUSED(pOutput); + + // Bitvector of enabled channels + const unsigned int maxChannels = 32; + unsigned int masterOutput = 0; + unsigned int headphoneOutput = 0; + + // Prepare each channel for output + processChannels(&masterOutput, &headphoneOutput, iBufferSize); + + // Compute headphone mix + // Head phone left/right mix + CSAMPLE cf_val = head_mix->get(); + CSAMPLE chead_gain = 0.5*(-cf_val+1.); + CSAMPLE cmaster_gain = 0.5*(cf_val+1.); + // qDebug() << "head val " << cf_val << ", head " << chead_gain + // << ", master " << cmaster_gain; + // Mix all the enabled headphone channels together. m_headphoneGain.setGain(chead_gain); @@ -522,7 +519,7 @@ void EngineMaster::addChannel(EngineChannel* pChannel) { SampleUtil::applyGain(pChannelInfo->m_pBuffer, 0, MAX_BUFFER_LEN); m_channels.push_back(pChannelInfo); - m_pMasterSync->addDeck(pChannel->getGroup()); + m_pMasterSync->addChannel(pChannel); EngineBuffer* pBuffer = pChannelInfo->m_pChannel->getEngineBuffer(); if (pBuffer != NULL) { @@ -542,9 +539,6 @@ EngineChannel* EngineMaster::getChannel(QString group) { return NULL; } -int EngineMaster::numChannels() const { - return m_channels.size(); -} const CSAMPLE* EngineMaster::getDeckBuffer(unsigned int i) const { return getChannelBuffer(PlayerManager::groupForDeck(i)); } diff --git a/src/engine/enginemaster.h b/src/engine/enginemaster.h index 15dbcebb5091..a9de430517e7 100644 --- a/src/engine/enginemaster.h +++ b/src/engine/enginemaster.h @@ -53,13 +53,9 @@ class EngineMaster : public EngineObject, public AudioSource { // Get access to the sample buffers. None of these are thread safe. Only to // be called by SoundManager. - int numChannels() const; const CSAMPLE* buffer(AudioOutput output) const; void process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize); - - EngineSync* getMasterSync(void); - void setMasterSync(QString deck); // Add an EngineChannel to the mixing engine. This is not thread safe -- // only call it before the engine has started mixing. @@ -136,6 +132,14 @@ class EngineMaster : public EngineObject, public AudioSource { void mixChannels(unsigned int channelBitvector, unsigned int maxChannels, CSAMPLE* pOutput, unsigned int iBufferSize, GainCalculator* pGainCalculator); + // Processes active channels. The master sync channel (if any) is processed + // first and all others are processed after. Sets the i'th bit of + // masterOutput and headphoneOutput if the i'th channel is enabled for the + // master output or headphone output, respectively. + void processChannels(unsigned int* masterOutput, + unsigned int* headphoneOutput, + int iBufferSize); + QList m_channels; CSAMPLE *m_pMaster, *m_pHead; diff --git a/src/engine/enginesync.cpp b/src/engine/enginesync.cpp index 9d575cf5ddea..d1098a6706c9 100644 --- a/src/engine/enginesync.cpp +++ b/src/engine/enginesync.cpp @@ -24,23 +24,67 @@ #include "engine/enginebuffer.h" #include "engine/enginechannel.h" #include "engine/enginecontrol.h" -#include "engine/enginemaster.h" #include "engine/enginesync.h" -EngineSync::EngineSync(EngineMaster *master, - ConfigObject* _config) : - EngineControl("[Master]", _config), - m_pEngineMaster(master), - m_pSourceRate(NULL), - m_pSourceBeatDistance(NULL), - m_sSyncSource("[Master]"), - m_dSourceRate(0.0f), //has to be zero so that master bpm gets set correctly on startup - m_dMasterBpm(124.0f), - m_dPseudoBufferPos(0.0f) -{ - m_pMasterBeatDistance = new ControlObject(ConfigKey("[Master]", "beat_distance")); - - m_pSampleRate = ControlObject::getControl(ConfigKey("[Master]","samplerate")); +static const char* kMasterSyncGroup = "[Master]"; + +SyncChannel::SyncChannel(EngineChannel* pChannel) + : m_pChannel(pChannel), + m_group(pChannel->getGroup()) { + m_pChannelSyncState = new ControlObject(ConfigKey(m_group, "sync_state")); + connect(m_pChannelSyncState, SIGNAL(valueChanged(double)), + this, SLOT(slotChannelSyncStateChanged(double)), + Qt::DirectConnection); + connect(m_pChannelSyncState, SIGNAL(valueChangedFromEngine(double)), + this, SLOT(slotChannelSyncStateChanged(double)), + Qt::DirectConnection); + + m_pFileBpm = ControlObject::getControl(ConfigKey(m_group, "file_bpm")); + m_pRateEngine = ControlObject::getControl(ConfigKey(m_group, "rateEngine")); + m_pBeatDistance = ControlObject::getControl(ConfigKey(m_group, "beat_distance")); +} + +SyncChannel::~SyncChannel() { + delete m_pChannelSyncState; +} + +EngineChannel* SyncChannel::getChannel() { + return m_pChannel; +} + +double SyncChannel::getFileBpm() const { + return m_pFileBpm ? m_pFileBpm->get() : 0.0; +} + +double SyncChannel::getState() const { + return m_pChannelSyncState->get(); +} + +void SyncChannel::setState(double state) { + m_pChannelSyncState->set(state); +} + +void SyncChannel::slotChannelSyncStateChanged(double v) { + emit(channelSyncStateChanged(this, v)); +} + +ControlObject* SyncChannel::getRateEngineControl() { + return m_pRateEngine; +} + +ControlObject* SyncChannel::getBeatDistanceControl() { + return m_pBeatDistance; +} + +EngineSync::EngineSync(ConfigObject* _config) + : EngineControl(kMasterSyncGroup, _config), + m_sSyncSource(kMasterSyncGroup), + m_dSourceRate(0.0f), //has to be zero so that master bpm gets set correctly on startup + m_dMasterBpm(124.0f), + m_dPseudoBufferPos(0.0f) { + m_pMasterBeatDistance = new ControlObject(ConfigKey(kMasterSyncGroup, "beat_distance")); + + m_pSampleRate = ControlObject::getControl(ConfigKey(kMasterSyncGroup, "samplerate")); connect(m_pSampleRate, SIGNAL(valueChangedFromEngine(double)), this, SLOT(slotSampleRateChanged(double)), Qt::DirectConnection); @@ -53,7 +97,7 @@ EngineSync::EngineSync(EngineMaster *master, m_iSampleRate = 44100; } - m_pMasterBpm = new ControlObject(ConfigKey("[Master]", "sync_bpm")); + m_pMasterBpm = new ControlObject(ConfigKey(kMasterSyncGroup, "sync_bpm")); connect(m_pMasterBpm, SIGNAL(valueChanged(double)), this, SLOT(slotMasterBpmChanged(double)), Qt::DirectConnection); @@ -61,13 +105,13 @@ EngineSync::EngineSync(EngineMaster *master, this, SLOT(slotMasterBpmChanged(double)), Qt::DirectConnection); - m_pSyncInternalEnabled = new ControlPushButton(ConfigKey("[Master]", "sync_master")); + m_pSyncInternalEnabled = new ControlPushButton(ConfigKey(kMasterSyncGroup, "sync_master")); m_pSyncInternalEnabled->setButtonMode(ControlPushButton::TOGGLE); connect(m_pSyncInternalEnabled, SIGNAL(valueChanged(double)), this, SLOT(slotInternalMasterChanged(double)), Qt::DirectConnection); - m_pSyncRateSlider = new ControlPotmeter(ConfigKey("[Master]", "rate"), 40.0, 200.0); + m_pSyncRateSlider = new ControlPotmeter(ConfigKey(kMasterSyncGroup, "rate"), 40.0, 200.0); connect(m_pSyncRateSlider, SIGNAL(valueChanged(double)), this, SLOT(slotSyncRateSliderChanged(double)), Qt::DirectConnection); @@ -78,251 +122,203 @@ EngineSync::EngineSync(EngineMaster *master, } EngineSync::~EngineSync() { + while (!m_channels.isEmpty()) { + delete m_channels.takeLast(); + } delete m_pMasterBpm; delete m_pMasterBeatDistance; delete m_pSyncRateSlider; } -void EngineSync::addDeck(QString deck) { - if (m_sDeckList.contains(deck)) { - qDebug() << "EngineSync: already has deck for deck" << deck; - return; +void EngineSync::addChannel(EngineChannel* pChannel) { + const QString& group = pChannel->getGroup(); + foreach (const SyncChannel* pChannel, m_channels) { + if (pChannel->getGroup() == group) { + qDebug() << "EngineSync: already has channel for" << group; + return; + } } - m_sDeckList.append(deck); - - // Connect objects so we can react when the user changes the settings - ConfigKey key(deck, "sync_state"); - ControlObject *deck_sync_state = ControlObject::getControl(key); - if (key.group == "[Channel1]") { - connect(deck_sync_state, SIGNAL(valueChanged(double)), - this, SLOT(slotDeck1StateChanged(double)), - Qt::DirectConnection); - connect(deck_sync_state, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotDeck1StateChanged(double)), - Qt::DirectConnection); - } else if (key.group == "[Channel2]") { - connect(deck_sync_state, SIGNAL(valueChanged(double)), - this, SLOT(slotDeck2StateChanged(double)), + SyncChannel* pSyncChannel = new SyncChannel(pChannel); + connect(pSyncChannel, SIGNAL(channelSyncStateChanged(SyncChannel*, double)), + this, SLOT(slotChannelSyncStateChanged(SyncChannel*, double)), Qt::DirectConnection); - connect(deck_sync_state, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotDeck2StateChanged(double)), - Qt::DirectConnection); - } else if (key.group == "[Channel3]") { - connect(deck_sync_state, SIGNAL(valueChanged(double)), - this, SLOT(slotDeck3StateChanged(double)), - Qt::DirectConnection); - connect(deck_sync_state, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotDeck3StateChanged(double)), - Qt::DirectConnection); - } else if (key.group == "[Channel4]") { - connect(deck_sync_state, SIGNAL(valueChanged(double)), - this, SLOT(slotDeck4StateChanged(double)), - Qt::DirectConnection); - connect(deck_sync_state, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotDeck4StateChanged(double)), - Qt::DirectConnection); - } else { - qDebug() << "ERROR not a known deck, can't hook up for master sync"; - return; - } - - -} - -void EngineSync::disconnectMaster() { - if (m_pSourceRate != NULL) { - m_pSourceRate->disconnect(); - m_pSourceRate = NULL; - } - if (m_pSourceBeatDistance != NULL) { - m_pSourceBeatDistance->disconnect(); - m_pSourceBeatDistance = NULL; - } - qDebug() << "UNSETTING master buffer (disconnected master)"; - m_pMasterChannel = NULL; + m_channels.append(pSyncChannel); } +void EngineSync::disableChannelMaster(const QString& channel) { + qDebug() << "UNSETTING master channel (disconnected master)"; -void EngineSync::disableDeckMaster(QString deck) { - if (deck == "") { - foreach (QString deck, m_sDeckList) { - if (deck != "[Master]") { - // Unset master on *all* other decks -- sometimes we end up with two masters - // for some reason. - ControlObject *sync_state = ControlObject::getControl(ConfigKey(deck, "sync_state")); - if (sync_state != NULL && sync_state->get() == SYNC_MASTER) { - sync_state->set(SYNC_SLAVE); - } - } + SyncChannel* pOldChannelMaster = m_pChannelMaster; + if (pOldChannelMaster) { + ControlObject* pSourceRate = pOldChannelMaster->getRateEngineControl(); + if (pSourceRate != NULL) { + disconnect(pSourceRate, SIGNAL(valueChangedFromEngine(double)), + this, SLOT(slotSourceRateChanged(double))); } - } else { - //qDebug() << "disabling" << deck << "as master"; - ControlObject *sync_state = ControlObject::getControl(ConfigKey(deck, "sync_state")); - if (sync_state == NULL) { - qDebug() << "Error: couldn't get sync_state for deck, couldn't disable master" << deck; - return; + ControlObject* pSourceBeatDistance = pOldChannelMaster->getBeatDistanceControl(); + if (pSourceBeatDistance != NULL) { + disconnect(pSourceBeatDistance, SIGNAL(valueChangedFromEngine(double)), + this, SLOT(slotSourceBeatDistanceChanged(double))); } - if (sync_state->get() == SYNC_MASTER) { - //qDebug() << deck << "notifying deck it is not master"; - sync_state->set(SYNC_SLAVE); + } + m_pChannelMaster = NULL; + + bool channelIsEmpty = channel.isEmpty(); + foreach (SyncChannel* pChannel, m_channels) { + // If channel is empty, unset master on *all* other channels -- sometimes we + // end up with two masters for some reason. + if (channelIsEmpty || pChannel->getGroup() == channel) { + if (pChannel->getState() == SYNC_MASTER) { + pChannel->setState(SYNC_SLAVE); + } } } } -void EngineSync::setMaster(QString group) { +void EngineSync::setMaster(const QString& group) { // Convenience function that can split out to either set internal // or set deck master. - // TODO(owen): midi master? or is that just internal? - if (group == "[Master]") { + if (group == kMasterSyncGroup) { setInternalMaster(); } else { - if (!setDeckMaster(group)) { + SyncChannel* pSyncChannel = getSyncChannelForGroup(group); + if (!setChannelMaster(pSyncChannel)) { qDebug() << "WARNING: failed to set selected master" << group << ", going with Internal instead"; setInternalMaster(); } } } -void EngineSync::setInternalMaster(void) { - if (m_sSyncSource == "[Master]") { +void EngineSync::setInternalMaster() { + if (m_sSyncSource == kMasterSyncGroup) { qDebug() << "already internal master"; return; } m_dMasterBpm = m_pMasterBpm->get(); QString old_master = m_sSyncSource; - m_sSyncSource = "[Master]"; + m_sSyncSource = kMasterSyncGroup; resetInternalBeatDistance(); - disableDeckMaster(old_master); - disconnectMaster(); + disableChannelMaster(old_master); updateSamplesPerBeat(); // This is all we have to do, we'll start using the pseudoposition right away. m_pSyncInternalEnabled->set(TRUE); } -bool EngineSync::setDeckMaster(QString deck) { - if (deck.isEmpty()) { - //qDebug() << "----------------------------------------------------unsetting master (got empty deck spec)"; - disconnectMaster(); - setInternalMaster(); - return true; +bool EngineSync::setChannelMaster(SyncChannel* pSyncChannel) { + // If a channel is master, disable it. + disableChannelMaster(m_sSyncSource); + + if (pSyncChannel == NULL) { + return false; + } + + // Only accept channels with an EngineBuffer. + EngineChannel* pChannel = pSyncChannel->getChannel(); + if (pChannel == NULL || pChannel->getEngineBuffer() == NULL) { + return false; } - EngineChannel* pChannel = m_pEngineMaster->getChannel(deck); + const QString& group = pChannel->getGroup(); + // Only consider channels that have a track loaded and are in the master // mix. - //qDebug() << "***********************************************asked to set a new master:" << deck; + m_pChannelMaster = pSyncChannel; + m_sSyncSource = group; - if (pChannel) { - disconnectMaster(); - if (pChannel->getEngineBuffer() == NULL) { - return false; - } - m_pMasterChannel = pChannel; - m_pSourceRate = ControlObject::getControl(ConfigKey(deck, "rateEngine")); - if (m_pSourceRate == NULL) { - //qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!! source true rate was null"; - return false; - } - connect(m_pSourceRate, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotSourceRateChanged(double)), - Qt::DirectConnection); - - m_pSourceBeatDistance = ControlObject::getControl(ConfigKey(deck, "beat_distance")); - if (m_pSourceBeatDistance == NULL) { - qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1 source beat dist was null"; - return false; - } - connect(m_pSourceBeatDistance, SIGNAL(valueChangedFromEngine(double)), - this, SLOT(slotSourceBeatDistanceChanged(double)), - Qt::DirectConnection); - - resetInternalBeatDistance(); //reset internal beat distance to equal the new master - //qDebug() << "----------------------------setting new master" << deck; - m_sSyncSource = deck; - m_pSyncInternalEnabled->set(FALSE); - slotSourceRateChanged(m_pSourceRate->get()); - // This is not redundant, I swear. Make sure lights are all up to date - ControlObject::getControl(ConfigKey(deck, "sync_master"))->set(TRUE); - ControlObject::getControl(ConfigKey(deck, "sync_slave"))->set(FALSE); - return true; - } else { - //qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!did not set master"; - if (pChannel == NULL) - qDebug() << "well, it was null!"; - else - qDebug() << pChannel << pChannel->isActive() << pChannel->isMaster(); + ControlObject* pSourceRate = pSyncChannel->getRateEngineControl(); + if (pSourceRate == NULL) { + //qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!! source true rate was null"; + return false; } + connect(pSourceRate, SIGNAL(valueChangedFromEngine(double)), + this, SLOT(slotSourceRateChanged(double)), + Qt::DirectConnection); - return false; -} + ControlObject* pSourceBeatDistance = pSyncChannel->getBeatDistanceControl(); + if (pSourceBeatDistance == NULL) { + qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1 source beat dist was null"; + return false; + } + connect(pSourceBeatDistance, SIGNAL(valueChangedFromEngine(double)), + this, SLOT(slotSourceBeatDistanceChanged(double)), + Qt::DirectConnection); -bool EngineSync::setMidiMaster() { - // Stub for now. - return false; + // reset internal beat distance to equal the new master + resetInternalBeatDistance(); + + //qDebug() << "----------------------------setting new master" << group; + + m_pSyncInternalEnabled->set(FALSE); + slotSourceRateChanged(pSourceRate->get()); + + // This is not redundant, I swear. Make sure lights are all up to date + ControlObject::getControl(ConfigKey(group, "sync_master"))->set(TRUE); + ControlObject::getControl(ConfigKey(group, "sync_slave"))->set(FALSE); + + return true; } -QString EngineSync::chooseNewMaster(QString dontpick="") { +QString EngineSync::chooseNewMaster(const QString& dontpick) { //qDebug() << "----------=-=-=-=-=-=-=-finding a new master"; - QString fallback = "[Master]"; - foreach (QString deck, m_sDeckList) { - if (deck == dontpick) { + QString fallback = kMasterSyncGroup; + foreach (SyncChannel* pSyncChannel, m_channels) { + const QString& group = pSyncChannel->getGroup(); + if (group == dontpick) { continue; } - ControlObject *sync_state = ControlObject::getControl(ConfigKey(deck, "sync_state")); - if (sync_state != NULL) { - double state = sync_state->get(); - if (state == SYNC_MASTER) { - qDebug() << "already have a new master" << deck; - return deck; - } else if (state == SYNC_NONE) { - continue; - } + double sync_state = pSyncChannel->getState(); + if (sync_state == SYNC_MASTER) { + qDebug() << "already have a new master" << group; + return group; + } else if (sync_state == SYNC_NONE) { + continue; } - EngineChannel* pChannel = m_pEngineMaster->getChannel(deck); + + EngineChannel* pChannel = pSyncChannel->getChannel(); if (pChannel && pChannel->isActive() && pChannel->isMaster()) { EngineBuffer* pBuffer = pChannel->getEngineBuffer(); if (pBuffer && pBuffer->getBpm() > 0) { - // If the deck is playing then go with it immediately. + // If the channel is playing then go with it immediately. if (fabs(pBuffer->getRate()) > 0) { - //qDebug() << "picked a new master deck:" << deck; - return deck; + //qDebug() << "picked a new master deck:" << group; + return group; } } } } - return fallback; } void EngineSync::slotSourceRateChanged(double rate_engine) { //master buffer can be null due to timing issues - if (m_pMasterChannel != NULL && rate_engine != m_dSourceRate) { + if (m_pChannelMaster != NULL && rate_engine != m_dSourceRate) { m_dSourceRate = rate_engine; - - double filebpm = m_pMasterChannel->getEngineBuffer()->getFileBpm(); + double filebpm = m_pChannelMaster->getFileBpm(); m_dMasterBpm = rate_engine * filebpm; + //qDebug() << "file bpm " << filebpm; //qDebug()<< "announcing a master bpm of" << m_dMasterBpm; if (m_dMasterBpm != 0) { m_pSyncRateSlider->set(m_dMasterBpm); } - m_pMasterBpm->set(m_dMasterBpm); //this will trigger all of the slaves to change rate + // This will trigger all of the slaves to change rate. + m_pMasterBpm->set(m_dMasterBpm); } } void EngineSync::slotSourceBeatDistanceChanged(double beat_dist) { - //pass it on to slaves and update internal position marker + // Pass it on to slaves and update internal position marker. m_pMasterBeatDistance->set(beat_dist); setPseudoPosition(beat_dist); } void EngineSync::slotSyncRateSliderChanged(double new_bpm) { - if (m_sSyncSource != "[Master]") { + if (m_sSyncSource != kMasterSyncGroup) { // TODO: this should be prevented by setting the slider to disabled. m_pSyncRateSlider->set(m_dMasterBpm); return; @@ -336,7 +332,7 @@ void EngineSync::slotMasterBpmChanged(double new_bpm) { m_pSyncRateSlider->set(new_bpm); if (new_bpm != m_dMasterBpm) { // qDebug() << "set slider"; - if (m_sSyncSource != "[Master]") { + if (m_sSyncSource != kMasterSyncGroup) { //qDebug() << "can't set master sync when sync isn't internal"; //XXX(Owen): //it looks like this is Good Enough for preventing accidental @@ -386,58 +382,46 @@ void EngineSync::slotInternalMasterChanged(double state) { setInternalMaster(); } else { //internal has been turned off. pick a slave - setMaster(chooseNewMaster()); + setMaster(chooseNewMaster("")); } } -void EngineSync::slotDeck1StateChanged(double state) { - deckXStateChanged("[Channel1]", state); -} - -void EngineSync::slotDeck2StateChanged(double state) { - deckXStateChanged("[Channel2]", state); -} - -void EngineSync::slotDeck3StateChanged(double state) { - deckXStateChanged("[Channel3]", state); -} - -void EngineSync::slotDeck4StateChanged(double state) { - deckXStateChanged("[Channel4]", state); -} +void EngineSync::slotChannelSyncStateChanged(SyncChannel* pSyncChannel, double state) { + if (!pSyncChannel) { + return; + } + const QString& group = pSyncChannel->getGroup(); + qDebug() << "got a master state change from" << group << state; -void EngineSync::deckXStateChanged(QString group, double state) { - qDebug() << "got a master state change from" << group; + const bool channelIsMaster = m_sSyncSource == group; - // In the following logic, m_sSyncSourcea acts like "previous sync source". + // In the following logic, m_sSyncSource acts like "previous sync source". if (state == SYNC_MASTER) { // TODO: don't allow setting of master if not playing - // Figure out who the old master was and turn them off - QString old_master = m_sSyncSource; - setDeckMaster(group); - qDebug() << "disabling previous master " << old_master; - if (old_master != "[Master]") { - disableDeckMaster(old_master); + // If setting this channel as master fails, pick a new master. + if (!setChannelMaster(pSyncChannel)) { + setMaster(chooseNewMaster(group)); } } else if (state == SYNC_SLAVE) { // Was this deck master before? If so do a handoff - ControlObject *sync_state = ControlObject::getControl(ConfigKey(group, "sync_state")); - if (m_sSyncSource == group) { + if (channelIsMaster) { qDebug() << group << " current master, setting us to slave (choose new)"; - sync_state->set(SYNC_SLAVE); + // TODO(rryan) isn't this redundant? Is this because of MIDI light + // breakage? + pSyncChannel->setState(SYNC_SLAVE); //choose a new master, but don't pick the current one! setMaster(chooseNewMaster(group)); } } else { // if we were the master, choose a new one. - if (m_sSyncSource == group) { + if (channelIsMaster) { qDebug() << group << " current master being set to none, choose new"; - setMaster(chooseNewMaster()); + setMaster(chooseNewMaster("")); } } } -double EngineSync::getInternalBeatDistance(void) const { +double EngineSync::getInternalBeatDistance() const { //returns number of samples distance from the last beat. if (m_dPseudoBufferPos < 0) { qDebug() << "ERROR: Internal beat distance should never be less than zero"; @@ -447,16 +431,16 @@ double EngineSync::getInternalBeatDistance(void) const { } void EngineSync::resetInternalBeatDistance() { - if (m_pSourceBeatDistance != NULL) { - m_dPseudoBufferPos = m_pSourceBeatDistance->get() * m_dSamplesPerBeat; - qDebug() << "Resetting internal beat distance to new master" << m_dPseudoBufferPos << " " - << m_pSourceBeatDistance->get(); - } else { - m_dPseudoBufferPos = 0; - } + ControlObject* pSourceBeatDistance = m_pChannelMaster != NULL ? + m_pChannelMaster->getBeatDistanceControl() : NULL; + double beat_distance = pSourceBeatDistance ? pSourceBeatDistance->get() : 0; + + m_dPseudoBufferPos = beat_distance * m_dSamplesPerBeat; + qDebug() << "Resetting internal beat distance to new master" + << m_dPseudoBufferPos << beat_distance; } -void EngineSync::updateSamplesPerBeat(void) { +void EngineSync::updateSamplesPerBeat() { //to get samples per beat, do: // // samples samples 60 seconds minutes @@ -475,20 +459,20 @@ void EngineSync::updateSamplesPerBeat(void) { } } -void EngineSync::incrementPseudoPosition(int bufferSize) { - // Enginemaster calls this function, it is used to keep track of the internal - // clock (when there is no other master like a deck or MIDI - // the pseudo position is a double because we want to be precise, - // and bpms may not line up exactly with samples. +void EngineSync::onCallbackStart(int bufferSize) { + // EngineMaster calls this function, it is used to keep track of the + // internal clock (when there is no other master like a deck or MIDI) the + // pseudo position is a double because we want to be precise, and beats may + // not line up exactly with samples. - if (m_sSyncSource != "[Master]") { - //we don't care, it will get set in setPseudoPosition + if (m_sSyncSource != kMasterSyncGroup) { + // We don't care, it will get set in setPseudoPosition. return; } - m_dPseudoBufferPos += bufferSize / 2; //stereo samples, so divide by 2 + m_dPseudoBufferPos += bufferSize / 2; // stereo samples, so divide by 2 - //can't use mod because we're in double land + // can't use mod because we're in double land if (m_dSamplesPerBeat <= 0) { qDebug() << "ERROR: Calculated <= 0 samples per beat which is impossible. Forcibly " << "setting to about 124 bpm at 44.1Khz."; @@ -506,5 +490,14 @@ void EngineSync::setPseudoPosition(double percent) { } EngineChannel* EngineSync::getMaster() const { - return m_pMasterChannel; + return m_pChannelMaster ? m_pChannelMaster->getChannel() : NULL; +} + +SyncChannel* EngineSync::getSyncChannelForGroup(const QString& group) { + foreach (SyncChannel* pChannel, m_channels) { + if (pChannel->getGroup() == group) { + return pChannel; + } + } + return NULL; } diff --git a/src/engine/enginesync.h b/src/engine/enginesync.h index 9aa3dfb4d53b..b7a935b4b159 100644 --- a/src/engine/enginesync.h +++ b/src/engine/enginesync.h @@ -22,7 +22,6 @@ #include "engine/enginecontrol.h" class EngineChannel; -class EngineMaster; class ControlObject; class ControlPushButton; class ControlPotmeter; @@ -33,21 +32,49 @@ enum SYNC_STATE { SYNC_MASTER = 2 }; +class SyncChannel : public QObject { + Q_OBJECT + public: + SyncChannel(EngineChannel* pChannel); + virtual ~SyncChannel(); + + const QString& getGroup() const { + return m_group; + } + + EngineChannel* getChannel(); + void setState(double state); + double getState() const; + double getFileBpm() const; + + ControlObject* getRateEngineControl(); + ControlObject* getBeatDistanceControl(); + + signals: + void channelSyncStateChanged(SyncChannel*, double); + + private slots: + void slotChannelSyncStateChanged(double); + + private: + EngineChannel* m_pChannel; + QString m_group; + ControlObject* m_pChannelSyncState; + ControlObject* m_pFileBpm; + ControlObject* m_pRateEngine; + ControlObject* m_pBeatDistance; +}; + class EngineSync : public EngineControl { Q_OBJECT public: - EngineSync(EngineMaster *master, ConfigObject* pConfig); + explicit EngineSync(ConfigObject* pConfig); virtual ~EngineSync(); - void addDeck(QString group); - void setMaster(QString group); - bool setDeckMaster(QString deck); - void setInternalMaster(void); - bool setMidiMaster(void); - EngineChannel* getMaster() const; - void incrementPseudoPosition(int bufferSize); - double getInternalBeatDistance(void) const; + void addChannel(EngineChannel* pChannel); + EngineChannel* getMaster() const; + void onCallbackStart(int bufferSize); private slots: void slotMasterBpmChanged(double); @@ -56,35 +83,33 @@ class EngineSync : public EngineControl { void slotSourceBeatDistanceChanged(double); void slotSampleRateChanged(double); void slotInternalMasterChanged(double); - void slotDeck1StateChanged(double); - void slotDeck2StateChanged(double); - void slotDeck3StateChanged(double); - void slotDeck4StateChanged(double); - - protected: - QString chooseNewMaster(QString dontpick); - void disconnectMaster(void); - void disableDeckMaster(QString deck); - void updateSamplesPerBeat(void); + void slotChannelSyncStateChanged(SyncChannel*, double); + + private: + void setMaster(const QString& group); + bool setChannelMaster(SyncChannel* pSyncChannel); + void setInternalMaster(); + QString chooseNewMaster(const QString& dontpick); + void disableChannelMaster(const QString& deck); + void updateSamplesPerBeat(); void setPseudoPosition(double percent); - void resetInternalBeatDistance(void); - void deckXStateChanged(QString group, double); + void resetInternalBeatDistance(); + double getInternalBeatDistance() const; + SyncChannel* getSyncChannelForGroup(const QString& group); + + SyncChannel* m_pChannelMaster; - EngineMaster* m_pEngineMaster; - EngineChannel* m_pMasterChannel; - ControlObject* m_pSourceRate; ControlObject* m_pMasterBpm; - ControlObject* m_pSourceBeatDistance; ControlObject* m_pMasterBeatDistance; ControlObject* m_pSampleRate; ControlPushButton* m_pSyncInternalEnabled; ControlPotmeter* m_pSyncRateSlider; - QList m_sDeckList; - + QList m_channels; QString m_sSyncSource; int m_iSampleRate; - double m_dSourceRate, m_dMasterBpm; + double m_dSourceRate; + double m_dMasterBpm; double m_dSamplesPerBeat; double m_dPseudoBufferPos; };