diff --git a/mixxx/src/basetrackplayer.cpp b/mixxx/src/basetrackplayer.cpp index b3cea64c268..6febd8f3677 100644 --- a/mixxx/src/basetrackplayer.cpp +++ b/mixxx/src/basetrackplayer.cpp @@ -109,8 +109,15 @@ BaseTrackPlayer::BaseTrackPlayer(QObject* pParent, ControlObject::getControl(ConfigKey(group, "play"))); } +void BaseTrackPlayer::finalize() { + m_pChannel->finalize(); + EngineBuffer* pEngineBuffer = m_pChannel->getEngineBuffer(); + pEngineBuffer->slotControlStop(1.0); +} + BaseTrackPlayer::~BaseTrackPlayer() { + qDebug() << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~BASETRACKPLAYER"; if (m_pLoadedTrack) { emit(unloadingTrack(m_pLoadedTrack)); m_pLoadedTrack.clear(); diff --git a/mixxx/src/basetrackplayer.h b/mixxx/src/basetrackplayer.h index 7514f02a79d..6930fe82f9f 100644 --- a/mixxx/src/basetrackplayer.h +++ b/mixxx/src/basetrackplayer.h @@ -31,6 +31,9 @@ class BaseTrackPlayer : public BasePlayer { // connected. Delete me when EngineMaster supports AudioInput assigning. EngineDeck* getEngineDeck() const; + // Tell the player we are about to delete it. + void finalize(); + public slots: void slotLoadTrack(TrackPointer track, bool bPlay=false); void slotFinishLoading(TrackPointer pTrackInfoObject); diff --git a/mixxx/src/deck.cpp b/mixxx/src/deck.cpp index 4815827683e..2697d3bc987 100644 --- a/mixxx/src/deck.cpp +++ b/mixxx/src/deck.cpp @@ -10,5 +10,5 @@ Deck::Deck(QObject* pParent, } Deck::~Deck() { + qDebug() << "deck deleteeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed"; } - diff --git a/mixxx/src/engine/enginebuffer.cpp b/mixxx/src/engine/enginebuffer.cpp index e1fdc66d224..c44a3b060a4 100644 --- a/mixxx/src/engine/enginebuffer.cpp +++ b/mixxx/src/engine/enginebuffer.cpp @@ -235,6 +235,7 @@ EngineBuffer::EngineBuffer(const char * _group, ConfigObject * _con EngineBuffer::~EngineBuffer() { + qDebug() << "~~~~~~~~~~~~~~~~~~~~~~~~~~~enginebuffer"; #ifdef __SCALER_DEBUG__ //close the writer df.close(); diff --git a/mixxx/src/engine/enginechannel.cpp b/mixxx/src/engine/enginechannel.cpp index 4399034c995..9013c24cf3f 100644 --- a/mixxx/src/engine/enginechannel.cpp +++ b/mixxx/src/engine/enginechannel.cpp @@ -22,7 +22,8 @@ EngineChannel::EngineChannel(const char* pGroup, EngineChannel::ChannelOrientation defaultOrientation) - : m_group(pGroup) { + : m_group(pGroup), + m_bDying(false) { m_pPFL = new ControlPushButton(ConfigKey(m_group, "pfl")); m_pPFL->setButtonMode(ControlPushButton::TOGGLE); m_pMaster = new ControlPushButton(ConfigKey(m_group, "master")); diff --git a/mixxx/src/engine/enginechannel.h b/mixxx/src/engine/enginechannel.h index b9d9211a707..c458a623b46 100644 --- a/mixxx/src/engine/enginechannel.h +++ b/mixxx/src/engine/enginechannel.h @@ -52,6 +52,9 @@ class EngineChannel : public EngineObject { virtual bool isPFL(); virtual bool isMaster(); + void finalize() { m_bDying = true; qDebug() << "DYING NOW " << m_group;} + bool isDying() const { return m_bDying; } + virtual void process(const CSAMPLE *pIn, const CSAMPLE *pOut, const int iBufferSize) = 0; // TODO(XXX) This hack needs to be removed. @@ -64,6 +67,7 @@ class EngineChannel : public EngineObject { ControlPushButton* m_pMaster; ControlPushButton* m_pPFL; ControlObject* m_pOrientation; + bool m_bDying; }; #endif diff --git a/mixxx/src/engine/enginedeck.cpp b/mixxx/src/engine/enginedeck.cpp index eecbb138490..50aaab640d7 100644 --- a/mixxx/src/engine/enginedeck.cpp +++ b/mixxx/src/engine/enginedeck.cpp @@ -36,13 +36,13 @@ EngineDeck::EngineDeck(const char* group, m_pPassing(new ControlPushButton(ConfigKey(group, "passthrough_enabled"))), // Need a +1 here because the CircularBuffer only allows its size-1 // items to be held at once (it keeps a blank spot open persistently) - m_sampleBuffer(MAX_BUFFER_LEN+1) { + m_sampleBuffer(MAX_BUFFER_LEN+1), + m_bPassthroughIsActive(false), + m_bPassthroughWasActive(false) { // Set up passthrough utilities and fields m_pPassing->setButtonMode(ControlPushButton::TOGGLE); m_pConversionBuffer = SampleUtil::alloc(MAX_BUFFER_LEN); - m_bPassthroughIsActive = false; - m_bPassthroughWasActive = false; // Set up passthrough toggle button connect(m_pPassing, SIGNAL(valueChanged(double)), @@ -73,6 +73,7 @@ EngineDeck::~EngineDeck() { } void EngineDeck::process(const CSAMPLE*, const CSAMPLE * pOutput, const int iBufferSize) { + if (isDying()) return; CSAMPLE* pOut = const_cast(pOutput); // Feed the incoming audio through if passthrough is active @@ -119,6 +120,7 @@ EngineBuffer* EngineDeck::getEngineBuffer() { } bool EngineDeck::isActive() { + if (isDying()) return false; if (m_bPassthroughWasActive && !m_bPassthroughIsActive) { return true; } @@ -128,7 +130,7 @@ bool EngineDeck::isActive() { void EngineDeck::receiveBuffer(AudioInput input, const short* pBuffer, unsigned int nFrames) { // Skip receiving audio input if passthrough is not active - if (!m_bPassthroughIsActive) { + if (!m_bPassthroughIsActive || isDying()) { return; } diff --git a/mixxx/src/engine/enginemaster.cpp b/mixxx/src/engine/enginemaster.cpp index ea599867edd..476e0ec6abb 100644 --- a/mixxx/src/engine/enginemaster.cpp +++ b/mixxx/src/engine/enginemaster.cpp @@ -349,12 +349,19 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff Timer timer("EngineMaster::process channels"); QList::iterator it = m_channels.begin(); + QList dying_channels; + for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; + QString group = pChannelInfo->m_pChannel->getGroup(); if (!pChannel->isActive()) { + if (pChannel->isDying()) { + qDebug() << "this channel is DYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYING " << group << " " << channel_number; + dying_channels.push_back(pChannelInfo); + } continue; } @@ -440,6 +447,11 @@ void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBuff // We're close to the end of the callback. Wake up the engine worker // scheduler so that it runs the workers. m_pWorkerScheduler->runWorkers(); + + foreach (ChannelInfo* p, dying_channels) { + qDebug() << "we are killing a channel!"; + removeChannelInfo(p); + } } void EngineMaster::addChannel(EngineChannel* pChannel) { @@ -501,3 +513,14 @@ const CSAMPLE* EngineMaster::buffer(AudioOutput output) const { return NULL; } } + +void EngineMaster::removeChannelInfo(ChannelInfo* pChannelInfo) { + QString group = pChannelInfo->m_pChannel->getGroup(); + qDebug() << "OFFICIALLY DELETING SHIT NOWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW " << group; + m_channels.removeAll(pChannelInfo); + SampleUtil::free(pChannelInfo->m_pBuffer); + delete pChannelInfo->m_pChannel; + delete pChannelInfo->m_pVolumeControl; + delete pChannelInfo; + emit(DeckRemoved(group)); +} diff --git a/mixxx/src/engine/enginemaster.h b/mixxx/src/engine/enginemaster.h index 0087c80315a..11e48e75c41 100644 --- a/mixxx/src/engine/enginemaster.h +++ b/mixxx/src/engine/enginemaster.h @@ -84,6 +84,9 @@ class EngineMaster : public EngineObject, public AudioSource { return m_pSideChain; } + signals: + void DeckRemoved(QString group); + private: struct ChannelInfo { EngineChannel* m_pChannel; @@ -130,6 +133,9 @@ class EngineMaster : public EngineObject, public AudioSource { void mixChannels(unsigned int channelBitvector, unsigned int maxChannels, CSAMPLE* pOutput, unsigned int iBufferSize, GainCalculator* pGainCalculator); + + void removeChannelInfo(ChannelInfo* pChannel); + QList m_channels; CSAMPLE *m_pMaster, *m_pHead; diff --git a/mixxx/src/engine/enginemicrophone.cpp b/mixxx/src/engine/enginemicrophone.cpp index c14a4c1939a..8b2a382f4b4 100644 --- a/mixxx/src/engine/enginemicrophone.cpp +++ b/mixxx/src/engine/enginemicrophone.cpp @@ -29,6 +29,7 @@ EngineMicrophone::~EngineMicrophone() { } bool EngineMicrophone::isActive() { + if (isDying()) { return false; } bool enabled = m_pEnabled->get() > 0.0; return enabled && !m_sampleBuffer.isEmpty(); } @@ -65,7 +66,7 @@ void EngineMicrophone::onInputDisconnected(AudioInput input) { } void EngineMicrophone::receiveBuffer(AudioInput input, const short* pBuffer, unsigned int iNumSamples) { - + if (isDying()) return; if (input.getType() != AudioPath::MICROPHONE || AudioInput::channelsNeededForType(input.getType()) != 1) { // This is an error! @@ -106,6 +107,7 @@ void EngineMicrophone::receiveBuffer(AudioInput input, const short* pBuffer, uns void EngineMicrophone::process(const CSAMPLE* pInput, const CSAMPLE* pOutput, const int iBufferSize) { Q_UNUSED(pInput); + if (isDying()) return; CSAMPLE* pOut = const_cast(pOutput); // If talkover is enabled, then read into the output buffer. Otherwise, skip diff --git a/mixxx/src/engine/enginepassthrough.cpp b/mixxx/src/engine/enginepassthrough.cpp index 4a1b3fa83ef..4e1f6677a85 100644 --- a/mixxx/src/engine/enginepassthrough.cpp +++ b/mixxx/src/engine/enginepassthrough.cpp @@ -30,6 +30,7 @@ EnginePassthrough::~EnginePassthrough() { } bool EnginePassthrough::isActive() { + if (isDying()) { return false; } bool enabled = m_pEnabled->get() > 0.0; return enabled && !m_sampleBuffer.isEmpty(); } @@ -63,7 +64,7 @@ void EnginePassthrough::onInputDisconnected(AudioInput input) { } void EnginePassthrough::receiveBuffer(AudioInput input, const short* pBuffer, unsigned int nFrames) { - + if (isDying()) return; if (input.getType() != AudioPath::EXTPASSTHROUGH) { // This is an error! qDebug() << "WARNING: EnginePassthrough receieved an AudioInput for a non-passthrough type!"; @@ -96,6 +97,7 @@ void EnginePassthrough::receiveBuffer(AudioInput input, const short* pBuffer, un } void EnginePassthrough::process(const CSAMPLE* pInput, const CSAMPLE* pOutput, const int iBufferSize) { + if (isDying()) return; CSAMPLE* pOut = const_cast(pOutput); Q_UNUSED(pInput); diff --git a/mixxx/src/engine/enginepregain.cpp b/mixxx/src/engine/enginepregain.cpp index ffcdbbd4c28..f3ec6fa4186 100644 --- a/mixxx/src/engine/enginepregain.cpp +++ b/mixxx/src/engine/enginepregain.cpp @@ -48,6 +48,7 @@ EnginePregain::EnginePregain(const char * group) EnginePregain::~EnginePregain() { + qDebug() << "DELETED PREGAIN"; delete potmeterPregain; delete m_pControlReplayGain; delete m_pTotalGain; diff --git a/mixxx/src/mixxx.cpp b/mixxx/src/mixxx.cpp index 725d03b6459..0cbb1939c59 100644 --- a/mixxx/src/mixxx.cpp +++ b/mixxx/src/mixxx.cpp @@ -638,10 +638,9 @@ MixxxApp::~MixxxApp() ControlObject::getControls(&leakedControls); if (leakedControls.size() > 0) { - qDebug() << "WARNING: The following" << leakedControls.size() << "controls were leaked:"; + qDebug() << "WARNING: " << leakedControls.size() << "controls were leaked"; foreach (ControlObject* pControl, leakedControls) { ConfigKey key = pControl->getKey(); - qDebug() << key.group << key.item; leakedConfigKeys.append(key); } diff --git a/mixxx/src/playermanager.cpp b/mixxx/src/playermanager.cpp index 6564fbf1f1f..aff7ec08a41 100644 --- a/mixxx/src/playermanager.cpp +++ b/mixxx/src/playermanager.cpp @@ -43,6 +43,10 @@ PlayerManager::PlayerManager(ConfigObject* pConfig, this, SLOT(slotNumPreviewDecksControlChanged(double)), Qt::DirectConnection); + connect(m_pEngine, SIGNAL(DeckRemoved(QString)), + this, SLOT(slotDeckRemoved(QString)), + Qt::DirectConnection); + // This is parented to the PlayerManager so does not need to be deleted SamplerBank* pSamplerBank = new SamplerBank(this); Q_UNUSED(pSamplerBank); @@ -166,12 +170,12 @@ void PlayerManager::slotNumDecksControlChanged(double v) { QMutexLocker locker(&m_mutex); int num = (int)v; if (num < m_decks.size()) { - // The request was invalid -- reset the value. - m_pCONumDecks->set(m_decks.size()); - qDebug() << "Ignoring request to reduce the number of decks to" << num; + qDebug() << "HOOOOOOOOOOOOOLY SHIT REMOVING " << m_decks.size() - num << " DECKS"; + initiateRemoveDecks(m_decks.size() - num); return; } + qDebug() << "ADDING DECKS SO " << m_decks.size() << " IS MORE LIKE " << num; while (m_decks.size() < num) { addDeckInner(); } @@ -207,8 +211,35 @@ void PlayerManager::slotNumPreviewDecksControlChanged(double v) { } } +void PlayerManager::slotDeckRemoved(QString group) { + QMutexLocker locker(&m_mutex); + Deck* deck = reinterpret_cast(m_players[group]); + if (deck == NULL) { + qDebug() << "Could not cast group " << group << " to a type Deck."; + return; + } + + m_players.remove(group); + QList::iterator it = m_decks.begin(); + for ( ; it != m_decks.end(); ++it) { + qDebug() << "compare" << std::hex << *it << " and " << deck; + if (*it == deck) { + qDebug() << "I FOUND IT TOTALLY AND STUFF"; + m_decks.erase(it); + + // We don't need to disconnect soundmanager connections -- that was already done when + // we initiated this delete. + delete deck; + return; + } + } + + qDebug() << "Couldn't find deck for group " << group << ", aborting delete"; +} + void PlayerManager::addDeck() { QMutexLocker locker(&m_mutex); + qDebug() << "DECK WAS ADDED MANUALLY AND STUFF"; addDeckInner(); m_pCONumDecks->set((double)m_decks.count()); } @@ -243,6 +274,25 @@ void PlayerManager::addDeckInner() { AudioInput(AudioInput::VINYLCONTROL, 0, number-1), pEngineDeck); } +void PlayerManager::initiateRemoveDecks(int count) { + // Do not lock m_mutex here. + if (m_decks.count() < count) { + qDebug() << "ERROR: Not enough decks to remove " << count << " decks."; + return; + } + + for (int i = 0; i < count; ++i) { + int number = m_decks.count() - i; + // EngineMaster will hear this signal and tell us to actually delete the deck. + Deck* deck = m_decks[number - 1]; + qDebug() << "removing deck " << number; + m_pSoundManager->unregisterOutput(AudioOutput(AudioOutput::DECK, 0, number)); + m_pSoundManager->unregisterInput(AudioInput(AudioInput::VINYLCONTROL, 0, number), + deck->getEngineDeck()); + deck->finalize(); + } +} + void PlayerManager::addSampler() { QMutexLocker locker(&m_mutex); addSamplerInner(); @@ -392,5 +442,3 @@ void PlayerManager::slotLoadTrackIntoNextAvailableSampler(TrackPointer pTrack) { it++; } } - - diff --git a/mixxx/src/playermanager.h b/mixxx/src/playermanager.h index 579408c74b1..c40d397620f 100644 --- a/mixxx/src/playermanager.h +++ b/mixxx/src/playermanager.h @@ -106,12 +106,18 @@ class PlayerManager : public QObject { signals: void loadLocationToPlayer(QString location, QString group); + private slots: + void slotDeckRemoved(QString group); + private: TrackPointer lookupTrack(QString location); // Must hold m_mutex before calling this method. Internal method that // creates a new deck. void addDeckInner(); // Must hold m_mutex before calling this method. Internal method that + // removes decks give by count starting from the end. + void initiateRemoveDecks(int count); + // Must hold m_mutex before calling this method. Internal method that // creates a new sampler. void addSamplerInner(); // Must hold m_mutex before calling this method. Internal method that diff --git a/mixxx/src/soundmanager.cpp b/mixxx/src/soundmanager.cpp index 2f7ed67598b..476a4f7e3ff 100644 --- a/mixxx/src/soundmanager.cpp +++ b/mixxx/src/soundmanager.cpp @@ -573,18 +573,28 @@ void SoundManager::registerOutput(AudioOutput output, const AudioSource *src) { emit(outputRegistered(output, src)); } -void SoundManager::registerInput(AudioInput input, AudioDestination *dest) { - if (m_registeredDestinations.contains(input)) { - // note that this can be totally ok if we just want a certain - // AudioInput to be going to a different AudioDest -bkgood - qDebug() << "WARNING: AudioInput already registered!"; +void SoundManager::unregisterOutput(AudioOutput output) { + if (!m_registeredSources.contains(output)) { + qDebug() << "WARNING: AudioOutput not registered, can't remove!"; + } else { + m_registeredSources.remove(output); } +} +void SoundManager::registerInput(AudioInput input, AudioDestination *dest) { m_registeredDestinations.insertMulti(input, dest); emit(inputRegistered(input, dest)); } +void SoundManager::unregisterInput(AudioInput input, AudioDestination *dest) { + if (!m_registeredDestinations.contains(input, dest)) { + qDebug() << "WARNING: AudioInput not registered, can't remove!"; + } else { + m_registeredDestinations.remove(input, dest); + } +} + QList SoundManager::registeredOutputs() const { return m_registeredSources.keys(); } diff --git a/mixxx/src/soundmanager.h b/mixxx/src/soundmanager.h index 501ecc915e3..e027009a8a7 100644 --- a/mixxx/src/soundmanager.h +++ b/mixxx/src/soundmanager.h @@ -97,6 +97,8 @@ class SoundManager : public QObject { void registerOutput(AudioOutput output, const AudioSource *src); void registerInput(AudioInput input, AudioDestination *dest); + void unregisterOutput(AudioOutput output); + void unregisterInput(AudioInput input, AudioDestination *dest); QList registeredOutputs() const; QList registeredInputs() const; @@ -126,7 +128,7 @@ class SoundManager : public QObject { SoundManagerConfig m_config; SoundDevice* m_pErrorDevice; QHash m_registeredSources; - QHash m_registeredDestinations; + QMultiHash m_registeredDestinations; ControlObject* m_pControlObjectSoundStatus; ControlObjectThreadMain* m_pControlObjectVinylControlGain; };