TrackPointer BaseExternalPlaylistModel::getTrack(const QModelIndex& index) const { QString location = index.sibling( index.row(), fieldIndex("location")).data().toString(); if (location.isEmpty()) { // Track is lost return TrackPointer(); } TrackDAO& track_dao = m_pTrackCollection->getTrackDAO(); int track_id = track_dao.getTrackId(location); bool track_already_in_library = track_id >= 0; if (track_id < 0) { // Add Track to library track_id = track_dao.addTrack(location, true); } TrackPointer pTrack; if (track_id < 0) { // Add Track to library failed, create a transient TrackInfoObject pTrack = TrackPointer(new TrackInfoObject(location), &QObject::deleteLater); } else { pTrack = track_dao.getTrack(track_id); } // If this track was not in the Mixxx library it is now added and will be // saved with the metadata from iTunes. If it was already in the library // then we do not touch it so that we do not over-write the user's metadata. if (!track_already_in_library) { QString artist = index.sibling( index.row(), fieldIndex("artist")).data().toString(); pTrack->setArtist(artist); QString title = index.sibling( index.row(), fieldIndex("title")).data().toString(); pTrack->setTitle(title); QString album = index.sibling( index.row(), fieldIndex("album")).data().toString(); pTrack->setAlbum(album); QString year = index.sibling( index.row(), fieldIndex("year")).data().toString(); pTrack->setYear(year); QString genre = index.sibling( index.row(), fieldIndex("genre")).data().toString(); pTrack->setGenre(genre); float bpm = index.sibling( index.row(), fieldIndex("bpm")).data().toString().toFloat(); pTrack->setBpm(bpm); } return pTrack; }
void CachingReader::run() { TrackPointer pLoadTrack; m_newTrackMutex.lock(); if (m_newTrack) { pLoadTrack = m_newTrack; m_newTrack = TrackPointer(); } m_newTrackMutex.unlock(); if (pLoadTrack) { loadTrack(pLoadTrack); } else { // Read the requested chunks. ChunkReadRequest request; ReaderStatusUpdate status; while (m_chunkReadRequestFIFO.read(&request, 1) == 1) { processChunkReadRequest(&request, &status); m_readerStatusFIFO.writeBlocking(&status, 1); } } // Notify the EngineWorkerScheduler that the work we did is done. setActive(false); }
TrackPointer GlobalTrackCache::revive( GlobalTrackCacheEntryPointer entryPtr) { TrackPointer savingPtr = entryPtr->getSavingWeakPtr().lock(); if (savingPtr) { if (traceLogEnabled()) { kLogger.trace() << "Found alive track" << entryPtr->getPlainPtr(); } return savingPtr; } // We are here if another thread is preempted during the // destructor of the last savingPtr referencing this // track, after the reference counter drops to zero and // before locking the cache. We need to revive it to abort // the deleter in the other thread. if (debugLogEnabled()) { kLogger.debug() << "Reviving zombie track" << entryPtr->getPlainPtr(); } DEBUG_ASSERT(entryPtr->getSavingWeakPtr().expired()); savingPtr = TrackPointer(entryPtr->getPlainPtr(), EvictAndSaveFunctor(entryPtr)); entryPtr->setSavingWeakPtr(savingPtr); return savingPtr; }
TrackPointer GlobalTrackCache::lookupByRef( const TrackRef& trackRef) { if (trackRef.hasId()) { return lookupById(trackRef.getId()); } else { const auto canonicalLocation = trackRef.getCanonicalLocation(); const auto trackByCanonicalLocation( m_tracksByCanonicalLocation.find(canonicalLocation)); if (m_tracksByCanonicalLocation.end() != trackByCanonicalLocation) { // Cache hit if (traceLogEnabled()) { kLogger.trace() << "Cache hit for" << canonicalLocation << trackByCanonicalLocation->second->getPlainPtr(); } return revive(trackByCanonicalLocation->second); } else { // Cache miss if (traceLogEnabled()) { kLogger.trace() << "Cache miss for" << canonicalLocation; } return TrackPointer(); } } }
void CachingReaderWorker::run() { unsigned static id = 0; //the id of this thread, for debugging purposes QThread::currentThread()->setObjectName(QString("CachingReaderWorker %1").arg(++id)); CachingReaderChunkReadRequest request; Event::start(m_tag); while (!load_atomic(m_stop)) { if (m_newTrack) { TrackPointer pLoadTrack; { // locking scope QMutexLocker locker(&m_newTrackMutex); pLoadTrack = m_newTrack; m_newTrack = TrackPointer(); } // implicitly unlocks the mutex loadTrack(pLoadTrack); } else if (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { // Read the requested chunk and send the result const ReaderStatusUpdate update(processReadRequest(request)); m_pReaderStatusFIFO->writeBlocking(&update, 1); } else { Event::end(m_tag); m_semaRun.acquire(); Event::start(m_tag); } } }
void EngineRecord::process(const CSAMPLE* pBuffer, const int iBufferSize) { // if recording is disabled if (m_recReady->get() == RECORD_OFF) { //qDebug("Setting record flag to: OFF"); if (fileOpen()) { closeFile(); //close file and free encoder emit(isRecording(false)); } } // if we are ready for recording, i.e, the output file has been selected, we // open a new file if (m_recReady->get() == RECORD_READY) { updateFromPreferences(); //update file location from pref if (openFile()) { qDebug("Setting record flag to: ON"); m_recReady->slotSet(RECORD_ON); emit(isRecording(true)); //will notify the RecordingManager // Since we just started recording, timeout and clear the metadata. m_iMetaDataLife = kMetaDataLifeTimeout; m_pCurrentTrack = TrackPointer(); if (m_bCueIsEnabled) { openCueFile(); m_cuesamplepos = 0; m_cuetrack = 0; } } else { // Maybe the encoder could not be initialized qDebug("Setting record flag to: OFF"); m_recReady->slotSet(RECORD_OFF); emit(isRecording(false)); } } // If recording is enabled process audio to compressed or uncompressed data. if (m_recReady->get() == RECORD_ON) { if (m_Encoding == ENCODING_WAVE || m_Encoding == ENCODING_AIFF) { if (m_sndfile != NULL) { sf_write_float(m_sndfile, pBuffer, iBufferSize); emit(bytesRecorded(iBufferSize)); } } else { if (m_encoder) { // Compress audio. Encoder will call method 'write()' below to // write a file stream m_encoder->encodeBuffer(pBuffer, iBufferSize); } } if (m_bCueIsEnabled) { if (metaDataHasChanged()) { m_cuetrack++; writeCueLine(); m_cuefile.flush(); } m_cuesamplepos += iBufferSize; } } }
EncoderFfmpegCore::EncoderFfmpegCore(EncoderCallback* pCallback, CodecID codec) #endif { m_bStreamInitialized = false; m_pCallback = pCallback; m_strMetaDataTitle = NULL; m_strMetaDataArtist = NULL; m_strMetaDataAlbum = NULL; m_pMetaData = TrackPointer(NULL); m_pEncodeFormatCtx = NULL; m_pEncoderAudioStream = NULL; m_pEncoderAudioCodec = NULL; m_pEncoderFormat = NULL; m_pSamples = NULL; m_pFltSamples = NULL; m_iAudioInputFrameSize = -1; memset(m_SBuffer, 0x00, (65355 * 2)); m_lBufferSize = 0; m_iAudioCpyLen = 0; m_iFltAudioCpyLen = 0; m_SCcodecId = codec; m_lBitrate = 128000; m_lDts = 0; m_lPts = 0; m_lRecordedBytes = 0; }
TrackPointer PlayerInfo::getCurrentPlayingTrack() { int deck = getCurrentPlayingDeck(); if (deck >= 0) { return getTrackInfo(PlayerManager::groupForDeck(deck)); } return TrackPointer(); }
TrackPointer BaseExternalTrackModel::getTrack(const QModelIndex& index) const { QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString(); QString title = index.sibling(index.row(), fieldIndex("title")).data().toString(); QString album = index.sibling(index.row(), fieldIndex("album")).data().toString(); QString year = index.sibling(index.row(), fieldIndex("year")).data().toString(); QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString(); float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat(); QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); if (location.isEmpty()) { // Track is lost return TrackPointer(); } bool track_already_in_library = false; TrackPointer pTrack = m_pTrackCollection->getTrackDAO() .getOrAddTrack(location, true, &track_already_in_library); // If this track was not in the Mixxx library it is now added and will be // saved with the metadata from iTunes. If it was already in the library // then we do not touch it so that we do not over-write the user's metadata. if (pTrack && !track_already_in_library) { pTrack->setArtist(artist); pTrack->setTitle(title); pTrack->setAlbum(album); pTrack->setYear(year); pTrack->setGenre(genre); pTrack->setBpm(bpm); } return pTrack; }
void CachingReaderWorker::run() { unsigned static id = 0; //the id of this thread, for debugging purposes QThread::currentThread()->setObjectName(QString("CachingReaderWorker %1").arg(++id)); TrackPointer pLoadTrack; ChunkReadRequest request; ReaderStatusUpdate status; Event::start(m_tag); while (!load_atomic(m_stop)) { if (m_newTrack) { m_newTrackMutex.lock(); pLoadTrack = m_newTrack; m_newTrack = TrackPointer(); m_newTrackMutex.unlock(); loadTrack(pLoadTrack); } else if (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { // Read the requested chunks. processChunkReadRequest(&request, &status); m_pReaderStatusFIFO->writeBlocking(&status, 1); } else { Event::end(m_tag); m_semaRun.acquire(); Event::start(m_tag); } } }
TrackPointer BaseTrackCache::lookupCachedTrack(TrackId trackId) const { // Only get the track from the TrackDAO if it's in the cache and marked as // dirty. if (m_bIsCaching && m_dirtyTracks.contains(trackId)) { return m_trackDAO.getTrack(trackId, true); } return TrackPointer(); }
TrackPointer PlayerInfo::getTrackInfo(const QString& group) { QMutexLocker locker(&m_mutex); if (m_loadedTrackMap.contains(group)) { return m_loadedTrackMap[group]; } return TrackPointer(); }
//static TrackPointer Track::newTemporary( const QFileInfo& fileInfo, const SecurityTokenPointer& pSecurityToken) { Track* pTrack = new Track( fileInfo, pSecurityToken, TrackId()); return TrackPointer(pTrack); }
//static TrackPointer TrackInfoObject::newTemporary( const QFileInfo& fileInfo, const SecurityTokenPointer& pSecurityToken) { return TrackPointer( new TrackInfoObject( fileInfo, pSecurityToken, TrackId()), &QObject::deleteLater); }
//static TrackPointer TrackInfoObject::newDummy( const QFileInfo& fileInfo, TrackId trackId) { return TrackPointer( new TrackInfoObject( fileInfo, SecurityTokenPointer(), trackId), &QObject::deleteLater); }
//static TrackPointer Track::newDummy( const QFileInfo& fileInfo, TrackId trackId) { Track* pTrack = new Track( fileInfo, SecurityTokenPointer(), trackId); return TrackPointer(pTrack); }
TrackPointer BrowseTableModel::getTrack(const QModelIndex& index) const { QString track_location = getTrackLocation(index); if (m_pRecordingManager->getRecordingLocation() == track_location) { QMessageBox::critical( 0, tr("Mixxx Library"), tr("Could not load the following file because" " it is in use by Mixxx or another application.") + "\n" +track_location); return TrackPointer(); } return m_pTrackCollection->getTrackDAO() .getOrAddTrack(track_location, true, NULL); }
/** Do a non-recursive import of all the songs in a directory. Does NOT decend into subdirectories. @param trackDao The track data access object which provides a connection to the database. We use this parameter in order to make this function callable from separate threads. You need to use a different DB connection for each thread. @return true if the scan completed without being cancelled. False if the scan was cancelled part-way through. */ bool TrackCollection::importDirectory(const QString& directory, TrackDAO& trackDao, const QStringList& nameFilters, volatile bool* cancel) { //qDebug() << "TrackCollection::importDirectory(" << directory<< ")"; emit(startedLoading()); // QFileInfoList files; //get a list of the contents of the directory and go through it. QDirIterator it(directory, nameFilters, QDir::Files | QDir::NoDotAndDotDot); while (it.hasNext()) { //If a flag was raised telling us to cancel the library scan then stop. if (*cancel) { return false; } QString absoluteFilePath = it.next(); // If the track is in the database, mark it as existing. This code gets exectuted // when other files in the same directory have changed (the directory hash has changed). trackDao.markTrackLocationAsVerified(absoluteFilePath); // If the file already exists in the database, continue and go on to // the next file. // If the file doesn't already exist in the database, then add // it. If it does exist in the database, then it is either in the // user's library OR the user has "removed" the track via // "Right-Click -> Remove". These tracks stay in the library, but // their mixxx_deleted column is 1. if (!trackDao.trackExistsInDatabase(absoluteFilePath)) { //qDebug() << "Loading" << it.fileName(); emit(progressLoading(it.fileName())); TrackPointer pTrack = TrackPointer(new TrackInfoObject( absoluteFilePath), &QObject::deleteLater); if (trackDao.addTracksAdd(pTrack.data(), false)) { // Successful added // signal the main instance of TrackDao, that there is a // new Track in the database m_trackDao->databaseTrackAdded(pTrack); } else { qDebug() << "Track ("+absoluteFilePath+") could not be added"; } } } emit(finishedLoading()); return true; }
void Library::slotLoadLocationToPlayer(QString location, QString group) { TrackDAO& track_dao = m_pTrackCollection->getTrackDAO(); int track_id = track_dao.getTrackId(location); if (track_id < 0) { // Add Track to library track_id = track_dao.addTrack(location, true); } TrackPointer pTrack; if (track_id < 0) { // Add Track to library failed, create a transient TrackInfoObject pTrack = TrackPointer(new TrackInfoObject(location), &QObject::deleteLater); } else { pTrack = track_dao.getTrack(track_id); } emit(loadTrackToPlayer(pTrack, group)); }
// This is called from the AnalyserQueue thread TrackPointer AnalyserQueue::dequeueNextBlocking() { m_qm.lock(); if (m_tioq.isEmpty()) { Event::end("AnalyserQueue process"); m_qwait.wait(&m_qm); Event::start("AnalyserQueue process"); if (m_exit) { m_qm.unlock(); return TrackPointer(); } } const PlayerInfo& info = PlayerInfo::instance(); TrackPointer pLoadTrack; QMutableListIterator<TrackPointer> it(m_tioq); while (it.hasNext()) { TrackPointer& pTrack = it.next(); if (!pTrack) { it.remove(); continue; } // Prioritize tracks that are loaded. if (info.isTrackLoaded(pTrack)) { qDebug() << "Prioritizing" << pTrack->getTitle() << pTrack->getLocation(); pLoadTrack = pTrack; it.remove(); break; } } if (!pLoadTrack && !m_tioq.isEmpty()) { pLoadTrack = m_tioq.dequeue(); } m_qm.unlock(); if (pLoadTrack) { qDebug() << "Analyzing" << pLoadTrack->getTitle() << pLoadTrack->getLocation(); } // pTrack might be NULL, up to the caller to check. return pLoadTrack; }
void CachingReaderWorker::run() { TrackPointer pLoadTrack; ChunkReadRequest request; ReaderStatusUpdate status; while (!deref(m_stop)) { if (m_newTrack) { m_newTrackMutex.lock(); pLoadTrack = m_newTrack; m_newTrack = TrackPointer(); m_newTrackMutex.unlock(); loadTrack(pLoadTrack); } else if (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { // Read the requested chunks. processChunkReadRequest(&request, &status); m_pReaderStatusFIFO->writeBlocking(&status, 1); } else { m_semaRun.acquire(); } } }
TrackPointer GlobalTrackCache::lookupById( const TrackId& trackId) { const auto trackById(m_tracksById.find(trackId)); if (m_tracksById.end() != trackById) { // Cache hit if (traceLogEnabled()) { kLogger.trace() << "Cache hit for" << trackId << trackById->second->getPlainPtr(); } return revive(trackById->second); } else { // Cache miss if (traceLogEnabled()) { kLogger.trace() << "Cache miss for" << trackId; } return TrackPointer(); } }
TrackPointer BaseTrackPlayerImpl::unloadTrack() { if (!m_pLoadedTrack) { // nothing to do return TrackPointer(); } // Save the loops that are currently set in a loop cue. If no loop cue is // currently on the track, then create a new one. double loopStart = m_pLoopInPoint->get(); double loopEnd = m_pLoopOutPoint->get(); if (loopStart != kNoTrigger && loopEnd != kNoTrigger && loopStart <= loopEnd) { CuePointer pLoopCue; QList<CuePointer> cuePoints(m_pLoadedTrack->getCuePoints()); QListIterator<CuePointer> it(cuePoints); while (it.hasNext()) { CuePointer pCue(it.next()); if (pCue->getType() == Cue::LOOP) { pLoopCue = pCue; } } if (!pLoopCue) { pLoopCue = m_pLoadedTrack->createAndAddCue(); pLoopCue->setType(Cue::LOOP); } pLoopCue->setPosition(loopStart); pLoopCue->setLength(loopEnd - loopStart); } disconnectLoadedTrack(); // Do not reset m_pReplayGain here, because the track might be still // playing and the last buffer will be processed. m_pPlay->set(0.0); TrackPointer pUnloadedTrack(std::move(m_pLoadedTrack)); DEBUG_ASSERT(!m_pLoadedTrack); return pUnloadedTrack; }
void AnalyserQueue::run() { unsigned static id = 0; //the id of this thread, for debugging purposes QThread::currentThread()->setObjectName(QString("AnalyserQueue %1").arg(++id)); // If there are no analyzers, don't waste time running. if (m_aq.size() == 0) return; m_progressInfo.current_track = TrackPointer(); m_progressInfo.track_progress = 0; m_progressInfo.queue_size = 0; m_progressInfo.sema.release(); // Initalise with one while (!m_exit) { TrackPointer nextTrack = dequeueNextBlocking(); // It's important to check for m_exit here in case we decided to exit // while blocking for a new track. if (m_exit) return; // If the track is NULL, try to get the next one. // Could happen if the track was queued but then deleted. // Or if dequeueNextBlocking is unblocked by exit == true if (!nextTrack) { m_qm.lock(); m_queue_size = m_tioq.size(); m_qm.unlock(); if (m_queue_size == 0) { emit(queueEmpty()); // emit asynchrony for no deadlock } continue; } Trace trace("AnalyserQueue analyzing track"); // Get the audio SoundSourceProxy soundSource(nextTrack); soundSource.open(); //Open the file for reading int iNumSamples = soundSource.length(); int iSampleRate = soundSource.getSampleRate(); if (iNumSamples == 0 || iSampleRate == 0) { qDebug() << "Skipping invalid file:" << nextTrack->getLocation(); continue; } QListIterator<Analyser*> it(m_aq); bool processTrack = false; while (it.hasNext()) { // Make sure not to short-circuit initialise(...) if (it.next()->initialise(nextTrack, iSampleRate, iNumSamples)) { processTrack = true; } } m_qm.lock(); m_queue_size = m_tioq.size(); m_qm.unlock(); if (processTrack) { emitUpdateProgress(nextTrack, 0); bool completed = doAnalysis(nextTrack, &soundSource); if (!completed) { //This track was cancelled QListIterator<Analyser*> itf(m_aq); while (itf.hasNext()) { itf.next()->cleanup(nextTrack); } queueAnalyseTrack(nextTrack); emitUpdateProgress(nextTrack, 0); } else { // 100% - FINALIZE_PERCENT finished emitUpdateProgress(nextTrack, 1000 - FINALIZE_PERCENT); // This takes around 3 sec on a Atom Netbook QListIterator<Analyser*> itf(m_aq); while (itf.hasNext()) { itf.next()->finalise(nextTrack); } emit(trackDone(nextTrack)); emitUpdateProgress(nextTrack, 1000); // 100% } } else { emitUpdateProgress(nextTrack, 1000); // 100% qDebug() << "Skipping track analysis because no analyzer initialized."; } m_qm.lock(); m_queue_size = m_tioq.size(); m_qm.unlock(); if (m_queue_size == 0) { emit(queueEmpty()); // emit asynchrony for no deadlock } } emit(queueEmpty()); // emit in case of exit; }
TEST_F(CoverArtUtilTest, searchImage) { // creating a temp track directory QString trackdir(QDir::tempPath() % "/TrackDir"); ASSERT_FALSE(QDir().exists(trackdir)); // it must start empty ASSERT_TRUE(QDir().mkpath(trackdir)); TrackPointer pTrack(Track::newTemporary(kTrackLocationTest)); SoundSourceProxy(pTrack).loadTrackMetadata(); QLinkedList<QFileInfo> covers; CoverArt res; // looking for cover in an empty directory res = CoverArtUtils::selectCoverArtForTrack(pTrack.data(), covers); CoverArt expected; expected.info.source = CoverInfo::GUESSED; EXPECT_EQ(expected, res); // Looking for a track with embedded cover. pTrack = TrackPointer(Track::newTemporary(kTrackLocationTest)); SoundSourceProxy(pTrack).loadTrackMetadataAndCoverArt(); expected = CoverArt(); expected.image = pTrack->getCoverArt().image; expected.info.type = CoverInfo::METADATA; expected.info.source = CoverInfo::GUESSED; expected.info.coverLocation = QString(); expected.info.hash = CoverArtUtils::calculateHash(expected.image); EXPECT_EQ(expected, pTrack->getCoverArt()); const char* format("jpg"); const QString qFormat(format); // Since we already parsed this image from the matadata in // kTrackLocationTest, hang on to it since we use it as a template for // stuff below. const QImage img = expected.image; QString trackBaseName = "cover-test"; QString trackAlbum = "album_name"; // Search Strategy // 0. If we have just one file, we will get it. // 1. %track-file-base%.jpg in the track directory for %track-file-base%.mp3 // 2. %album%.jpg // 3. cover.jpg // 4. front.jpg // 5. album.jpg // 6. folder.jpg // 7. if just one file exists take that otherwise none. // All the following expect the same image/hash to be selected. expected.image = img; expected.info.hash = CoverArtUtils::calculateHash(expected.image); // All the following expect FILE and GUESSED. expected.info.type = CoverInfo::FILE; expected.info.source = CoverInfo::GUESSED; // 0. saving just one cover in our temp track dir QString cLoc_foo = QString(trackdir % "/" % "foo." % qFormat); EXPECT_TRUE(img.save(cLoc_foo, format)); // looking for cover in an directory with one image will select that one. expected.image = QImage(cLoc_foo); expected.info.coverLocation = "foo.jpg"; expected.info.hash = CoverArtUtils::calculateHash(expected.image); covers << QFileInfo(cLoc_foo); res = CoverArtUtils::selectCoverArtForTrack(trackBaseName, trackAlbum, covers); EXPECT_EQ(expected, res); QFile::remove(cLoc_foo); QStringList extraCovers; // adding some extra images (bigger) just to populate the track dir. QString cLoc_big1 = QString(trackdir % "/" % "big1." % qFormat); EXPECT_TRUE(img.scaled(1000,1000).save(cLoc_big1, format)); extraCovers << cLoc_big1; QString cLoc_big2 = QString(trackdir % "/" % "big2." % qFormat); EXPECT_TRUE(img.scaled(900,900).save(cLoc_big2, format)); extraCovers << cLoc_big2; QString cLoc_big3 = QString(trackdir % "/" % "big3." % qFormat); EXPECT_TRUE(img.scaled(800,800).save(cLoc_big3, format)); extraCovers << cLoc_big3; // saving more covers using the preferred names in the right order QLinkedList<QFileInfo> prefCovers; // 1. track_filename.jpg QString cLoc_filename = QString(trackdir % "/cover-test." % qFormat); EXPECT_TRUE(img.scaled(500,500).save(cLoc_filename, format)); prefCovers << QFileInfo(cLoc_filename); // 2. album_name.jpg QString cLoc_albumName = QString(trackdir % "/album_name." % qFormat); EXPECT_TRUE(img.scaled(500,500).save(cLoc_albumName, format)); prefCovers << QFileInfo(cLoc_albumName); // 3. cover.jpg QString cLoc_cover = QString(trackdir % "/" % "cover." % qFormat); EXPECT_TRUE(img.scaled(400,400).save(cLoc_cover, format)); prefCovers << QFileInfo(cLoc_cover); // 4. front.jpg QString cLoc_front = QString(trackdir % "/" % "front." % qFormat); EXPECT_TRUE(img.scaled(300,300).save(cLoc_front, format)); prefCovers << QFileInfo(cLoc_front); // 5. album.jpg QString cLoc_album = QString(trackdir % "/" % "album." % qFormat); EXPECT_TRUE(img.scaled(100,100).save(cLoc_album, format)); prefCovers << QFileInfo(cLoc_album); // 6. folder.jpg QString cLoc_folder = QString(trackdir % "/" % "folder." % qFormat); EXPECT_TRUE(img.scaled(100,100).save(cLoc_folder, format)); prefCovers << QFileInfo(cLoc_folder); // 8. other1.jpg QString cLoc_other1 = QString(trackdir % "/" % "other1." % qFormat); EXPECT_TRUE(img.scaled(10,10).save(cLoc_other1, format)); prefCovers << QFileInfo(cLoc_other1); // 7. other2.jpg QString cLoc_other2 = QString(trackdir % "/" % "other2." % qFormat); EXPECT_TRUE(img.scaled(10,10).save(cLoc_other2, format)); prefCovers << QFileInfo(cLoc_other2); // we must find covers in the right order EXPECT_EQ(8, prefCovers.size()); // Remove the covers one by one from the front, checking that each one is // selected as we remove the previously-most-preferable cover. while (!prefCovers.isEmpty()) { QFileInfo cover = prefCovers.first(); // We expect no cover selected for other1 since there are 2 covers, // neither of which match our preferred cover names. other2 will be // selected once we get to it since it is the only cover available. if (cover.baseName() == "other1") { expected.image = QImage(); expected.info.type = CoverInfo::NONE; expected.info.coverLocation = QString(); expected.info.hash = 0; } else { expected.image = QImage(cover.filePath()); expected.info.type = CoverInfo::FILE; expected.info.coverLocation = cover.fileName(); expected.info.hash = CoverArtUtils::calculateHash(expected.image); } res = CoverArtUtils::selectCoverArtForTrack(trackBaseName, trackAlbum, prefCovers); EXPECT_QSTRING_EQ(expected.info.coverLocation, res.info.coverLocation); EXPECT_QSTRING_EQ(expected.info.hash, res.info.hash); EXPECT_EQ(expected, res); QFile::remove(cover.filePath()); prefCovers.pop_front(); } // // Additional tests // // what is chosen when cover.jpg and cover.JPG exists? // (it must always prefer the lighter cover) QString cLoc_coverJPG = trackdir % "/" % "cover." % "JPG"; EXPECT_TRUE(img.scaled(200,200).save(cLoc_coverJPG, "JPG")); prefCovers.append(QFileInfo(cLoc_coverJPG)); QString cLoc_coverjpg = trackdir % "/" % "cover." % "jpg"; EXPECT_TRUE(img.scaled(400,400).save(cLoc_coverjpg, "jpg")); prefCovers.append(QFileInfo(cLoc_coverjpg)); extraCovers << cLoc_coverJPG << cLoc_coverjpg; res = CoverArtUtils::selectCoverArtForTrack(trackBaseName, trackAlbum, prefCovers); expected.image = QImage(cLoc_coverJPG); expected.info.hash = CoverArtUtils::calculateHash(expected.image); expected.info.coverLocation = "cover.JPG"; EXPECT_EQ(expected, res); // As we are looking for %album%.jpg and %base_track.jpg%, // we need to check if everything works with UTF8 chars. trackBaseName = QString::fromUtf8("track_ðÑöæäî"); trackAlbum = QString::fromUtf8("öæäîðÑ_album"); prefCovers.clear(); // 2. album_name.jpg cLoc_albumName = QString(trackdir % "/" % trackAlbum % "." % qFormat); EXPECT_TRUE(img.save(cLoc_albumName, format)); prefCovers.append(QFileInfo(cLoc_albumName)); res = CoverArtUtils::selectCoverArtForTrack(trackBaseName, trackAlbum, prefCovers); expected.image = QImage(cLoc_albumName); expected.info.hash = CoverArtUtils::calculateHash(expected.image); expected.info.coverLocation = trackAlbum % ".jpg"; EXPECT_EQ(expected, res); // 1. track_filename.jpg cLoc_filename = QString(trackdir % "/" % trackBaseName % "." % qFormat); EXPECT_TRUE(img.save(cLoc_filename, format)); prefCovers.append(QFileInfo(cLoc_filename)); res = CoverArtUtils::selectCoverArtForTrack(trackBaseName, trackAlbum, prefCovers); expected.image = QImage(cLoc_filename); expected.info.hash = CoverArtUtils::calculateHash(expected.image); expected.info.coverLocation = trackBaseName % ".jpg"; EXPECT_EQ(expected, res); QFile::remove(cLoc_filename); QFile::remove(cLoc_albumName); // cleaning temp dir foreach (QString loc, extraCovers) { QFile::remove(loc); }
void WWaveformViewer::onTrackUnloaded( TrackPointer /*track*/) { if (m_waveformWidget) m_waveformWidget->setTrack(TrackPointer(0)); }
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)); }
TrackPointer ProxyTrackModel::getTrack(const QModelIndex& index) const { QModelIndex indexSource = mapToSource(index); return m_pTrackModel ? m_pTrackModel->getTrack(indexSource) : TrackPointer(); }
void ClockControl::trackUnloaded(TrackPointer pTrack) { Q_UNUSED(pTrack) trackLoaded(TrackPointer()); }
void SamplerBank::slotLoadSamplerBank(double v) { if (v == 0.0 || m_pPlayerManager == NULL) { return; } QString samplerBankPath = QFileDialog::getOpenFileName( NULL, tr("Load Sampler Bank"), QString(), tr("Mixxx Sampler Banks (*.xml)")); if (samplerBankPath.isEmpty()) { return; } // The user has picked a new directory via a file dialog. This means the // system sandboxer (if we are sandboxed) has granted us permission to this // folder. We don't need access to this file on a regular basis so we do not // register a security bookmark. QFile file(samplerBankPath); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(NULL, tr("Error Reading Sampler Bank"), tr("Could not open the sampler bank file '%1'.") .arg(samplerBankPath)); return; } QDomDocument doc; if (!doc.setContent(file.readAll())) { QMessageBox::warning(NULL, tr("Error Reading Sampler Bank"), tr("Could not read the sampler bank file '%1'.") .arg(samplerBankPath)); return; } QDomElement root = doc.documentElement(); if(root.tagName() != "samplerbank") { QMessageBox::warning(NULL, tr("Error Reading Sampler Bank"), tr("Could not read the sampler bank file '%1'.") .arg(samplerBankPath)); return; } QDomNode n = root.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); if (!e.isNull()) { if (e.tagName() == "sampler") { QString group = e.attribute("group", ""); QString location = e.attribute("location", ""); if (!group.isEmpty()) { if (location.isEmpty()) { m_pPlayerManager->slotLoadTrackToPlayer(TrackPointer(), group); } else { m_pPlayerManager->slotLoadToPlayer(location, group); } } } } n = n.nextSibling(); } file.close(); }