TrackRef GlobalTrackCache::initTrackId( const TrackPointer& strongPtr, TrackRef trackRef, TrackId trackId) { DEBUG_ASSERT(strongPtr); DEBUG_ASSERT(!trackRef.getId().isValid()); DEBUG_ASSERT(trackId.isValid()); TrackRef trackRefWithId(trackRef, trackId); EvictAndSaveFunctor* pDel = std::get_deleter<EvictAndSaveFunctor>(strongPtr); DEBUG_ASSERT(pDel); // Insert item by id DEBUG_ASSERT(m_tracksById.find(trackId) == m_tracksById.end()); m_tracksById.insert(std::make_pair( trackId, pDel->getCacheEntryPointer())); strongPtr->initId(trackId); DEBUG_ASSERT(createTrackRef(*strongPtr) == trackRefWithId); DEBUG_ASSERT(m_tracksById.find(trackId) != m_tracksById.end()); return trackRefWithId; }
bool AnalyzerWaveform::isDisabledOrLoadStoredSuccess(TrackPointer tio) const { ConstWaveformPointer pTrackWaveform = tio->getWaveform(); ConstWaveformPointer pTrackWaveformSummary = tio->getWaveformSummary(); ConstWaveformPointer pLoadedTrackWaveform; ConstWaveformPointer pLoadedTrackWaveformSummary; TrackId trackId = tio->getId(); bool missingWaveform = pTrackWaveform.isNull(); bool missingWavesummary = pTrackWaveformSummary.isNull(); if (trackId.isValid() && (missingWaveform || missingWavesummary)) { QList<AnalysisDao::AnalysisInfo> analyses = m_pAnalysisDao->getAnalysesForTrack(trackId); QListIterator<AnalysisDao::AnalysisInfo> it(analyses); while (it.hasNext()) { const AnalysisDao::AnalysisInfo& analysis = it.next(); WaveformFactory::VersionClass vc; if (analysis.type == AnalysisDao::TYPE_WAVEFORM) { vc = WaveformFactory::waveformVersionToVersionClass(analysis.version); if (missingWaveform && vc == WaveformFactory::VC_USE) { pLoadedTrackWaveform = ConstWaveformPointer( WaveformFactory::loadWaveformFromAnalysis(analysis)); missingWaveform = false; } else if (vc != WaveformFactory::VC_KEEP) { // remove all other Analysis except that one we should keep m_pAnalysisDao->deleteAnalysis(analysis.analysisId); } } if (analysis.type == AnalysisDao::TYPE_WAVESUMMARY) { vc = WaveformFactory::waveformSummaryVersionToVersionClass(analysis.version); if (missingWavesummary && vc == WaveformFactory::VC_USE) { pLoadedTrackWaveformSummary = ConstWaveformPointer( WaveformFactory::loadWaveformFromAnalysis(analysis)); missingWavesummary = false; } else if (vc != WaveformFactory::VC_KEEP) { // remove all other Analysis except that one we should keep m_pAnalysisDao->deleteAnalysis(analysis.analysisId); } } } } // If we don't need to calculate the waveform/wavesummary, skip. if (!missingWaveform && !missingWavesummary) { kLogger.debug() << "loadStored - Stored waveform loaded"; if (pLoadedTrackWaveform) { tio->setWaveform(pLoadedTrackWaveform); } if (pLoadedTrackWaveformSummary) { tio->setWaveformSummary(pLoadedTrackWaveformSummary); } return true; } return false; }
TEST_F(AutoDJProcessorTest, EnabledSuccess_PlayingDeck2_TrackLoadFailed) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); // Pretend a track is playing on deck 2. TrackPointer pTrack(newTestTrack(nextTrackId(testId))); // Load track and mark it playing. deck2.slotLoadTrack(pTrack, true); // Indicate the track loaded successfully. deck2.fakeTrackLoadedEvent(pTrack); // Arbitrary to check that it was unchanged. master.crossfader.set(0.2447); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); // The first track will fail to load and the second will succeed. pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), false)); AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); // No change to the crossfader or play states. EXPECT_DOUBLE_EQ(1, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // After the load failed signal we will receive another track load signal // for deck 1. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), false)); // Pretend the track load fails. deck1.slotLoadTrack(pTrack, false); deck1.fakeTrackLoadFailedEvent(pTrack); // No change to the mode, crossfader, or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(1, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Pretend the track load succeeds. deck1.slotLoadTrack(pTrack, false); deck1.fakeTrackLoadedEvent(pTrack); // No change to the mode, crossfader, or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); }
TEST_F(AutoDJProcessorTest, FadeToDeck2_SeekEnd) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); // Crossfader starts on the left. master.crossfader.set(-1.0); // Pretend a track is playing on deck 1. TrackPointer pTrack(newTestTrack(nextTrackId(testId))); // Load track and mark it playing. deck1.slotLoadTrack(pTrack, true); // Indicate the track loaded successfully. deck1.fakeTrackLoadedEvent(pTrack); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); pAutoDJTableModel->appendTrack(testId); EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel2]"), false)); // Enable AutoDJ, we immediately transition into IDLE and request a track // load on deck2. AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); // Pretend the track load succeeds. deck2.slotLoadTrack(pTrack, false); deck2.fakeTrackLoadedEvent(pTrack); // No change to the mode, crossfader or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Seek deck 2 to the very end 99 % deck2.playposition.set(0.99); // Expect that we will transition into P1FADING mode. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_P1FADING)); // Seek track to 99 % it should fade // not 100 % because the final step is done by deck2 deck1.playposition.set(0.99); EXPECT_EQ(AutoDJProcessor::ADJ_P1FADING, pProcessor->getState()); EXPECT_LT(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Deck 2 should been seeked back to a suitable position for crossfade EXPECT_GT(0.99, deck2.playposition.get()); }
QList<AnalysisDao::AnalysisInfo> AnalysisDao::getAnalysesForTrack(TrackId trackId) { if (!m_db.isOpen() || !trackId.isValid()) { return QList<AnalysisInfo>(); } QSqlQuery query(m_db); query.prepare(QString( "SELECT id, type, description, version, data_checksum FROM %1 " "WHERE track_id=:trackId").arg(s_analysisTableName)); query.bindValue(":trackId", trackId.toVariant()); return loadAnalysesFromQuery(trackId, &query); }
TEST_F(AutoDJProcessorTest, EnabledSuccess_DecksStopped) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); // Need two tracks -- one to be loaded in the left deck and one to load in // the right deck. pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); // Expect that we switch into ADJ_ENABLE_P1LOADED first. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_ENABLE_P1LOADED)); // Expect that we get a load-and-play signal for [Channel1]. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), true)); AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_ENABLE_P1LOADED, pProcessor->getState()); // Sets crossfader left and deck 1 playing. EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); // ADJ_ENABLE_P1LOADED logic does not set play directly. It waits for the // engine to load the track and set the deck playing. EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Expect that we transition to ADJ_IDLE. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); // Expect that we will receive a load call for [Channel2] after we get the // first playposition update from deck 1. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel2]"), false)); // Pretend a track loaded successfully and that it is now playing. This // triggers a call to AutoDJProcessor::playerPlayChanged and // AutoDJProcessor::playerPlaypositionChanged. We should switch to ADJ_IDLE // and queue a track to deck 2. // Load the track and mark it playing (as the loadTrackToPlayer signal would // have connected to this eventually). TrackPointer pTrack = collection()->getTrackDAO().getTrack(testId, false); deck1.slotLoadTrack(pTrack, true); // Signal that the request to load pTrack succeeded. deck1.fakeTrackLoadedEvent(pTrack); // Pretend the engine moved forward on the deck. deck1.playposition.set(0.1); // By now we will have transitioned to idle and requested a load to deck 2. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); }
void GlobalTrackCacheResolver::initTrackIdAndUnlockCache(TrackId trackId) { DEBUG_ASSERT(m_pInstance); DEBUG_ASSERT(GlobalTrackCacheLookupResult::NONE != m_lookupResult); DEBUG_ASSERT(m_strongPtr); DEBUG_ASSERT(trackId.isValid()); if (m_trackRef.getId().isValid()) { // Ignore initializing the same id twice DEBUG_ASSERT(m_trackRef.getId() == trackId); } else { m_trackRef = m_pInstance->initTrackId( m_strongPtr, m_trackRef, trackId); DEBUG_ASSERT(m_trackRef.getId() == trackId); } unlockCache(); DEBUG_ASSERT(m_trackRef == createTrackRef(*m_strongPtr)); }
bool AnalysisDao::deleteAnalysesForTrack(TrackId trackId) { if (!trackId.isValid()) { return false; } QSqlQuery query(m_db); query.prepare(QString( "SELECT id FROM %1 where track_id = :track_id").arg(s_analysisTableName)); query.bindValue(":track_id", trackId.toVariant()); if (!query.exec()) { LOG_FAILED_QUERY(query) << "couldn't delete analyses for track" << trackId; return false; } QList<int> analysesToDelete; const int idColumn = query.record().indexOf("id"); while (query.next()) { analysesToDelete.append( query.value(idColumn).toInt()); } foreach (int analysisId, analysesToDelete) { deleteAnalysis(analysisId); }
TEST_F(AutoDJProcessorTest, TrackZeroLength) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); // Need two tracks -- one to be loaded in the left deck and one to load in // the right deck. pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); // Expect that we switch into ADJ_ENABLE_P1LOADED first. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_ENABLE_P1LOADED)); // Expect that we get a load-and-play signal for [Channel1]. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), true)).Times(2); AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_ENABLE_P1LOADED, pProcessor->getState()); // Sets crossfader left and deck 1 playing. EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); // ADJ_ENABLE_P1LOADED logic does not set play directly. It waits for the // engine to load the track and set the deck playing. EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Pretend to load a faulty track with Duration = 0 // Load the track and mark it playing (as the loadTrackToPlayer signal would // have connected to this eventually). TrackPointer pTrack(newTestTrack(testId)); pTrack->setDuration(0); deck1.slotLoadTrack(pTrack, true); // Expect that the track is rejected an a new one is loaded // Signal that the request to load pTrack succeeded. deck1.fakeTrackLoadedEvent(pTrack); }
void GlobalTrackCache::resolve( GlobalTrackCacheResolver* /*in/out*/ pCacheResolver, QFileInfo /*in*/ fileInfo, TrackId trackId, SecurityTokenPointer pSecurityToken) { DEBUG_ASSERT(pCacheResolver); // Primary lookup by id (if available) if (trackId.isValid()) { if (debugLogEnabled()) { kLogger.debug() << "Resolving track by id" << trackId; } auto strongPtr = lookupById(trackId); if (strongPtr) { if (debugLogEnabled()) { kLogger.debug() << "Cache hit - found track by id" << trackId << strongPtr.get(); } TrackRef trackRef = createTrackRef(*strongPtr); pCacheResolver->initLookupResult( GlobalTrackCacheLookupResult::HIT, std::move(strongPtr), std::move(trackRef)); return; } } // Secondary lookup by canonical location // The TrackRef is constructed now after the lookup by ID failed to // avoid calculating the canonical file path if it is not needed. TrackRef trackRef = TrackRef::fromFileInfo(fileInfo, trackId); if (trackRef.hasCanonicalLocation()) { if (debugLogEnabled()) { kLogger.debug() << "Resolving track by canonical location" << trackRef.getCanonicalLocation(); } auto strongPtr = lookupByRef(trackRef); if (strongPtr) { // Cache hit if (debugLogEnabled()) { kLogger.debug() << "Cache hit - found track by canonical location" << trackRef.getCanonicalLocation() << strongPtr.get(); } pCacheResolver->initLookupResult( GlobalTrackCacheLookupResult::HIT, std::move(strongPtr), std::move(trackRef)); return; } } if (!m_pSaver) { // Do not allocate any new tracks once the cache // has been deactivated DEBUG_ASSERT(isEmpty()); kLogger.warning() << "Cache miss - caching has already been deactivated" << trackRef; return; } if (debugLogEnabled()) { kLogger.debug() << "Cache miss - allocating track" << trackRef; } auto deletingPtr = std::unique_ptr<Track, void (&)(Track*)>( new Track( std::move(fileInfo), std::move(pSecurityToken), std::move(trackId)), deleteTrack); // Track objects live together with the cache on the main thread // and will be deleted later within the event loop. But this // function might be called from any thread, even from worker // threads without an event loop. We need to move the newly // created object to the target thread. deletingPtr->moveToThread(QApplication::instance()->thread()); auto cacheEntryPtr = std::make_shared<GlobalTrackCacheEntry>( std::move(deletingPtr)); auto savingPtr = TrackPointer( cacheEntryPtr->getPlainPtr(), EvictAndSaveFunctor(cacheEntryPtr)); cacheEntryPtr->setSavingWeakPtr(savingPtr); if (debugLogEnabled()) { kLogger.debug() << "Cache miss - inserting new track into cache" << trackRef << deletingPtr.get(); } if (trackRef.hasId()) { // Insert item by id DEBUG_ASSERT(m_tracksById.find( trackRef.getId()) == m_tracksById.end()); m_tracksById.insert(std::make_pair( trackRef.getId(), cacheEntryPtr)); } if (trackRef.hasCanonicalLocation()) { // Insert item by track location DEBUG_ASSERT(m_tracksByCanonicalLocation.find( trackRef.getCanonicalLocation()) == m_tracksByCanonicalLocation.end()); m_tracksByCanonicalLocation.insert(std::make_pair( trackRef.getCanonicalLocation(), cacheEntryPtr)); } pCacheResolver->initLookupResult( GlobalTrackCacheLookupResult::MISS, std::move(savingPtr), std::move(trackRef)); }
TEST_F(AutoDJProcessorTest, FadeToDeck2_Long_Transition) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); // Crossfader starts on the left. master.crossfader.set(-1.0); // Pretend a track is playing on deck 1. TrackPointer pTrack(newTestTrack(nextTrackId(testId))); // Load track and mark it playing. deck1.slotLoadTrack(pTrack, true); // Indicate the track loaded successfully. deck1.fakeTrackLoadedEvent(pTrack); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); pAutoDJTableModel->appendTrack(testId); EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel2]"), false)); // Enable AutoDJ, we immediately transition into IDLE and request a track // load on deck2. AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); // Set a long transition time // The test tracks are only 30s pProcessor->setTransitionTime(60); // We expect that fading starts at the middle of the track // And Auto-DJ should keep running // Pretend the track load succeeds. deck2.slotLoadTrack(pTrack, false); deck2.fakeTrackLoadedEvent(pTrack); // No change to the mode, crossfader or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Seek track to 45 % is shoud not fade deck1.playposition.set(0.45); EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); // Expect that we will transition into P1FADING mode. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_P1FADING)); // Seek track to 55 % it should fade deck1.playposition.set(0.55); EXPECT_EQ(AutoDJProcessor::ADJ_P1FADING, pProcessor->getState()); EXPECT_LT(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Now start deck2 playing. deck2.playposition.set(0.1); // Seek track to End deck1.playposition.set(1.0); EXPECT_EQ(AutoDJProcessor::ADJ_P1FADING, pProcessor->getState()); qDebug() << "master.crossfader.get()" << master.crossfader.get(); // EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Expect that we will transition into IDLE mode and receive a track-load // request for deck 1 after deck 2 starts playing. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), false)); deck2.playposition.set(0.5); }
TEST_F(AutoDJProcessorTest, FadeToDeck2_LoadOnDeck1_TrackLoadFailed) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); // Crossfader starts on the left. master.crossfader.set(-1.0); // Pretend a track is playing on deck 1. TrackPointer pTrack(newTestTrack(nextTrackId(testId))); // Load track and mark it playing. deck1.slotLoadTrack(pTrack, true); // Indicate the track loaded successfully. deck1.fakeTrackLoadedEvent(pTrack); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); // The first track is loaded into deck 2, the second track is loaded into // deck 1 (fails) afer we fade to deck 2, and the third is loaded into deck // 1 (succeeds). pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel2]"), false)); // Enable AutoDJ, we immediately transition into IDLE and request a track // load on deck2. AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); // No change to the crossfader or play states. EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Pretend the track load succeeds. deck2.slotLoadTrack(pTrack, false); deck2.fakeTrackLoadedEvent(pTrack); // No change to the mode, crossfader or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Expect that we will transition into P1FADING mode. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_P1FADING)); // Pretend the track is over (should trigger an instant-fade). deck1.playposition.set(1.0); EXPECT_EQ(AutoDJProcessor::ADJ_P1FADING, pProcessor->getState()); EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Expect that we will transition into IDLE mode and receive a track-load // request for deck 1 after deck 2 starts playing. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), false)); // Now start deck2 playing. deck2.playposition.set(0.1); // Check we are in IDLE mode, the crossfader is fully right, and deck 2 is // playing. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Expect we will receive another track load request for deck 1 after we // fail the first request. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), false)); // Pretend the track load request fails. deck1.slotLoadTrack(pTrack, false); deck1.fakeTrackLoadFailedEvent(pTrack); // No change to the mode, crossfader, or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); // Pretend the track load request succeeds. deck1.slotLoadTrack(pTrack, false); deck1.fakeTrackLoadedEvent(pTrack); // No change to the mode, crossfader, or play states. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(1.0, deck2.play.get()); }
TEST_F(AutoDJProcessorTest, EnabledSuccess_DecksStopped_TrackLoadFailsRightDeck) { TrackId testId = addTrackToCollection(kTrackLocationTest); ASSERT_TRUE(testId.isValid()); PlaylistTableModel* pAutoDJTableModel = pProcessor->getTableModel(); // Need three tracks -- one to be loaded in the left deck (succeeding), one // to load in the righ deck (failing) and one to load in the right deck // (succeeding). pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); pAutoDJTableModel->appendTrack(testId); // Expect that we switch into ADJ_ENABLE_P1LOADED first. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_ENABLE_P1LOADED)); // Expect that we get a load-and-play signal for [Channel1]. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel1]"), true)); AutoDJProcessor::AutoDJError err = pProcessor->toggleAutoDJ(true); EXPECT_EQ(AutoDJProcessor::ADJ_OK, err); EXPECT_EQ(AutoDJProcessor::ADJ_ENABLE_P1LOADED, pProcessor->getState()); // Sets crossfader left. EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); // ADJ_ENABLE_P1LOADED logic does not set play directly. It waits for the // engine to load the track and set the deck playing. EXPECT_DOUBLE_EQ(0.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); // Load the track and mark it playing (as the loadTrackToPlayer signal would // have connected to this eventually). TrackPointer pTrack(newTestTrack(testId)); deck1.slotLoadTrack(pTrack, true); // Signal that the request to load pTrack to deck1 succeeded. deck1.fakeTrackLoadedEvent(pTrack); // Expect that we will switch into ADJ_IDLE. EXPECT_CALL(*pProcessor, emitAutoDJStateChanged(AutoDJProcessor::ADJ_IDLE)); // Expect that we will receive a load call for [Channel2] after we get the // first playposition update from deck 1. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel2]"), false)); // Pretend the engine moved forward on the deck. This will request a track // load on deck 2 and put us in IDLE mode. deck1.playposition.set(0.1); // Check that we are now in ADJ_IDLE mode. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); // Expect that we will receive another load call for [Channel2] after we get // the track load failed signal. EXPECT_CALL(*pProcessor, emitLoadTrackToPlayer(_, QString("[Channel2]"), false)); // Now pretend that the deck2 load request failed. deck2.slotLoadTrack(pTrack, false); deck2.fakeTrackLoadFailedEvent(pTrack); // Check that we are still in ADJ_IDLE mode. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); // Pretend the deck2 load request succeeded. deck2.slotLoadTrack(pTrack, false); deck2.fakeTrackLoadedEvent(pTrack); // Check that we are still in ADJ_IDLE mode and the left deck is playing. EXPECT_EQ(AutoDJProcessor::ADJ_IDLE, pProcessor->getState()); EXPECT_DOUBLE_EQ(-1.0, master.crossfader.get()); EXPECT_DOUBLE_EQ(1.0, deck1.play.get()); EXPECT_DOUBLE_EQ(0.0, deck2.play.get()); }