Redesigned FileAnalyzer and Analyzer task. New design is much cleaner, resulting in simpler code and faster file import performance. Also, playlist files are now handled ahead of everything else, which makes sure the progress indicator increases monotonically. Preliminary tests show that file import performance has been more than doubled! For example, the time for importing ~1000 files decreased from 116 seconds to 46 seconds on the development machine, which corresponds to a 2.5x speed-up.

This commit is contained in:
LoRd_MuldeR 2013-10-06 19:26:08 +02:00
parent a8cdb050fb
commit 9dbb317a5c
4 changed files with 272 additions and 346 deletions

View File

@ -25,6 +25,7 @@
#include "LockedFile.h" #include "LockedFile.h"
#include "Model_AudioFile.h" #include "Model_AudioFile.h"
#include "Thread_FileAnalyzer_Task.h" #include "Thread_FileAnalyzer_Task.h"
#include "PlaylistImporter.h"
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@ -35,6 +36,8 @@
#include <QImage> #include <QImage>
#include <QThreadPool> #include <QThreadPool>
#include <QTime> #include <QTime>
#include <QElapsedTimer>
#include <QTimer>
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -42,12 +45,22 @@
FileAnalyzer::FileAnalyzer(const QStringList &inputFiles) FileAnalyzer::FileAnalyzer(const QStringList &inputFiles)
: :
m_abortFlag(false), m_tasksCounterNext(0),
m_tasksCounterDone(0),
m_inputFiles(inputFiles), m_inputFiles(inputFiles),
m_templateFile(NULL) m_templateFile(NULL),
m_pool(NULL)
{ {
m_bSuccess = false; m_bSuccess = false;
m_bAborted = false; m_bAborted = false;
m_filesAccepted = 0;
m_filesRejected = 0;
m_filesDenied = 0;
m_filesDummyCDDA = 0;
m_filesCueSheet = 0;
m_timer = new QElapsedTimer;
} }
FileAnalyzer::~FileAnalyzer(void) FileAnalyzer::~FileAnalyzer(void)
@ -59,7 +72,13 @@ FileAnalyzer::~FileAnalyzer(void)
if(QFile::exists(templatePath)) QFile::remove(templatePath); if(QFile::exists(templatePath)) QFile::remove(templatePath);
} }
AnalyzeTask::reset(); if(!m_pool->waitForDone(2500))
{
qWarning("There are still running tasks in the thread pool!");
}
LAMEXP_DELETE(m_pool);
LAMEXP_DELETE(m_timer);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -109,18 +128,29 @@ const char *FileAnalyzer::g_tags_aud[] =
void FileAnalyzer::run() void FileAnalyzer::run()
{ {
m_abortFlag = false;
m_bAborted = false;
m_bSuccess = false; m_bSuccess = false;
int nFiles = m_inputFiles.count(); m_tasksCounterNext = 0;
m_tasksCounterDone = 0;
m_completedCounter = 0;
emit progressMaxChanged(nFiles); m_completedFiles.clear();
m_completedTaskIds.clear();
m_runningTaskIds.clear();
m_filesAccepted = 0;
m_filesRejected = 0;
m_filesDenied = 0;
m_filesDummyCDDA = 0;
m_filesCueSheet = 0;
m_timer->invalidate();
//Update progress
emit progressMaxChanged(m_inputFiles.count());
emit progressValChanged(0); emit progressValChanged(0);
lamexp_natural_string_sort(m_inputFiles, true); //.sort(); //Create MediaInfo template file
if(!m_templateFile) if(!m_templateFile)
{ {
if(!createTemplate()) if(!createTemplate())
@ -130,75 +160,106 @@ void FileAnalyzer::run()
} }
} }
AnalyzeTask::reset(); //Handle playlist files
QThreadPool *pool = new QThreadPool(); lamexp_natural_string_sort(m_inputFiles, true);
QThread::msleep(333); handlePlaylistFiles();
lamexp_natural_string_sort(m_inputFiles, true);
pool->setMaxThreadCount(qBound(2, ((QThread::idealThreadCount() * 3) / 2), 12)); const unsigned int nFiles = m_inputFiles.count();
while(!(m_inputFiles.isEmpty() || m_bAborted)) //Update progress
{ emit progressMaxChanged(nFiles);
while(!(m_inputFiles.isEmpty() || m_bAborted))
{
if(!AnalyzeTask::waitForFreeSlot(&m_abortFlag))
{
qWarning("Timeout in AnalyzeTask::waitForFreeSlot() !!!");
}
if(m_abortFlag) //Create thread pool
{ if(!m_pool) m_pool = new QThreadPool();
MessageBeep(MB_ICONERROR); m_pool->setMaxThreadCount(qBound(2, ((QThread::idealThreadCount() * 3) / 2), 12));
m_bAborted = true;
break;
}
if(!m_bAborted) //Start first N threads
{ QTimer::singleShot(0, this, SLOT(initializeTasks()));
const QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
AnalyzeTask *task = new AnalyzeTask(currentFile, m_templateFile->filePath(), &m_abortFlag); //Start event processing
connect(task, SIGNAL(fileSelected(QString)), this, SIGNAL(fileSelected(QString)), Qt::DirectConnection); this->exec();
connect(task, SIGNAL(progressValChanged(unsigned int)), this, SIGNAL(progressValChanged(unsigned int)), Qt::DirectConnection);
connect(task, SIGNAL(progressMaxChanged(unsigned int)), this, SIGNAL(progressMaxChanged(unsigned int)), Qt::DirectConnection);
connect(task, SIGNAL(fileAnalyzed(AudioFileModel)), this, SIGNAL(fileAnalyzed(AudioFileModel)), Qt::DirectConnection);
pool->start(task); //Wait for pending tasks to complete
m_pool->waitForDone();
if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
{
emit progressMaxChanged(nFiles += count);
}
}
}
//One of the Analyze tasks may have gathered additional files from a playlist!
if(!m_bAborted)
{
pool->waitForDone();
if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
{
emit progressMaxChanged(nFiles += count);
}
}
}
pool->waitForDone();
LAMEXP_DELETE(pool);
//Was opertaion aborted?
if(m_bAborted) if(m_bAborted)
{ {
qWarning("Operation cancelled by user!"); qWarning("Operation cancelled by user!");
return; return;
} }
//Update progress
emit progressValChanged(nFiles);
//Emit pending files (this should not be required though!)
if(!m_completedFiles.isEmpty())
{
qWarning("FileAnalyzer: Pending file information found after last thread terminated!");
QList<unsigned int> keys = m_completedFiles.keys(); qSort(keys);
while(!keys.isEmpty())
{
emit fileAnalyzed(m_completedFiles.take(keys.takeFirst()));
}
}
qDebug("All files added.\n"); qDebug("All files added.\n");
m_bSuccess = true; m_bSuccess = true;
QThread::msleep(333);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Privtae Functions // Privtae Functions
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool FileAnalyzer::analyzeNextFile(void)
{
if(!(m_inputFiles.isEmpty() || m_bAborted))
{
const unsigned int taskId = m_tasksCounterNext++;
const QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
if((!m_timer->isValid()) || (m_timer->elapsed() >= 250))
{
emit fileSelected(QFileInfo(currentFile).fileName());
m_timer->restart();
}
AnalyzeTask *task = new AnalyzeTask(taskId, currentFile, m_templateFile->filePath(), &m_bAborted);
connect(task, SIGNAL(fileAnalyzed(const unsigned int, const int, AudioFileModel)), this, SLOT(taskFileAnalyzed(unsigned int, const int, AudioFileModel)), Qt::QueuedConnection);
connect(task, SIGNAL(taskCompleted(const unsigned int)), this, SLOT(taskThreadFinish(const unsigned int)), Qt::QueuedConnection);
m_runningTaskIds.insert(taskId); m_pool->start(task);
return true;
}
return false;
}
void FileAnalyzer::handlePlaylistFiles(void)
{
QStringList importedFiles;
while(!m_inputFiles.isEmpty())
{
const QString currentFile = m_inputFiles.takeFirst();
if(!PlaylistImporter::importPlaylist(importedFiles, currentFile))
{
importedFiles << currentFile;
}
}
while(!importedFiles.isEmpty())
{
const QString currentFile = importedFiles.takeFirst();
if(!m_inputFiles.contains(currentFile, Qt::CaseInsensitive))
{
m_inputFiles << currentFile;
}
}
}
bool FileAnalyzer::createTemplate(void) bool FileAnalyzer::createTemplate(void)
{ {
if(m_templateFile) if(m_templateFile)
@ -251,33 +312,105 @@ bool FileAnalyzer::createTemplate(void)
return true; return true;
} }
////////////////////////////////////////////////////////////
// Slot Functions
////////////////////////////////////////////////////////////
void FileAnalyzer::initializeTasks(void)
{
for(int i = 0; i < m_pool->maxThreadCount(); i++)
{
if(!analyzeNextFile()) break;
}
}
void FileAnalyzer::taskFileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file)
{
m_completedTaskIds.insert(taskId);
switch(fileType)
{
case AnalyzeTask::fileTypeNormal:
m_filesAccepted++;
if(m_tasksCounterDone == taskId)
{
emit fileAnalyzed(file);
m_tasksCounterDone++;
}
else
{
m_completedFiles.insert(taskId, file);
}
break;
case AnalyzeTask::fileTypeCDDA:
m_filesDummyCDDA++;
break;
case AnalyzeTask::fileTypeDenied:
m_filesDenied++;
break;
case AnalyzeTask::fileTypeCueSheet:
m_filesCueSheet++;
break;
case AnalyzeTask::fileTypeUnknown:
m_filesRejected++;
break;
default:
throw "Unknown file type identifier!";
}
//Emit all pending files
while(m_completedTaskIds.contains(m_tasksCounterDone))
{
if(m_completedFiles.contains(m_tasksCounterDone))
{
emit fileAnalyzed(m_completedFiles.take(m_tasksCounterDone));
}
m_completedTaskIds.remove(m_tasksCounterDone);
m_tasksCounterDone++;
}
}
void FileAnalyzer::taskThreadFinish(const unsigned int taskId)
{
m_runningTaskIds.remove(taskId);
emit progressValChanged(++m_completedCounter);
if(!analyzeNextFile())
{
if(m_runningTaskIds.empty())
{
QTimer::singleShot(0, this, SLOT(quit())); //Stop event processing, if all threads have completed!
}
}
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Public Functions // Public Functions
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int FileAnalyzer::filesAccepted(void) unsigned int FileAnalyzer::filesAccepted(void)
{ {
return AnalyzeTask::filesAccepted(); return m_filesAccepted;
} }
unsigned int FileAnalyzer::filesRejected(void) unsigned int FileAnalyzer::filesRejected(void)
{ {
return AnalyzeTask::filesRejected(); return m_filesRejected;
} }
unsigned int FileAnalyzer::filesDenied(void) unsigned int FileAnalyzer::filesDenied(void)
{ {
return AnalyzeTask::filesDenied(); return m_filesDenied;
} }
unsigned int FileAnalyzer::filesDummyCDDA(void) unsigned int FileAnalyzer::filesDummyCDDA(void)
{ {
return AnalyzeTask::filesDummyCDDA(); return m_filesDummyCDDA;
} }
unsigned int FileAnalyzer::filesCueSheet(void) unsigned int FileAnalyzer::filesCueSheet(void)
{ {
return AnalyzeTask::filesCueSheet(); return m_filesCueSheet;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -25,12 +25,16 @@
#include <QThread> #include <QThread>
#include <QStringList> #include <QStringList>
#include <QHash>
#include <QSet>
class AudioFileModel; class AudioFileModel;
class QFile; class QFile;
class QDir; class QDir;
class QFileInfo; class QFileInfo;
class LockedFile; class LockedFile;
class QThreadPool;
class QElapsedTimer;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Splash Thread // Splash Thread
@ -44,7 +48,7 @@ public:
FileAnalyzer(const QStringList &inputFiles); FileAnalyzer(const QStringList &inputFiles);
~FileAnalyzer(void); ~FileAnalyzer(void);
void run(); void run();
bool getSuccess(void) { return !isRunning() && m_bSuccess; } bool getSuccess(void) { return (!isRunning()) && (!m_bAborted) && m_bSuccess; }
unsigned int filesAccepted(void); unsigned int filesAccepted(void);
unsigned int filesRejected(void); unsigned int filesRejected(void);
@ -59,20 +63,41 @@ signals:
void progressMaxChanged(unsigned int); void progressMaxChanged(unsigned int);
public slots: public slots:
void abortProcess(void) { m_abortFlag = true; } void abortProcess(void) { m_bAborted = true; exit(-1); }
private slots:
void initializeTasks(void);
void taskFileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file);
void taskThreadFinish(const unsigned int);
private: private:
const AudioFileModel analyzeFile(const QString &filePath, int *type); bool analyzeNextFile(void);
void handlePlaylistFiles(void);
bool createTemplate(void); bool createTemplate(void);
QThreadPool *m_pool;
QElapsedTimer *m_timer;
unsigned int m_tasksCounterNext;
unsigned int m_tasksCounterDone;
unsigned int m_completedCounter;
unsigned int m_filesAccepted;
unsigned int m_filesRejected;
unsigned int m_filesDenied;
unsigned int m_filesDummyCDDA;
unsigned int m_filesCueSheet;
QStringList m_inputFiles; QStringList m_inputFiles;
LockedFile *m_templateFile; LockedFile *m_templateFile;
volatile bool m_abortFlag; QSet<unsigned int> m_completedTaskIds;
QSet<unsigned int> m_runningTaskIds;
QHash<unsigned int, AudioFileModel> m_completedFiles;
static const char *g_tags_gen[]; static const char *g_tags_gen[];
static const char *g_tags_aud[]; static const char *g_tags_aud[];
bool m_bAborted; volatile bool m_bAborted;
bool m_bSuccess; volatile bool m_bSuccess;
}; };

View File

@ -24,7 +24,6 @@
#include "Global.h" #include "Global.h"
#include "LockedFile.h" #include "LockedFile.h"
#include "Model_AudioFile.h" #include "Model_AudioFile.h"
#include "PlaylistImporter.h"
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@ -45,35 +44,13 @@
#define IS_SEC(SEC) (key.startsWith((SEC "_"), Qt::CaseInsensitive)) #define IS_SEC(SEC) (key.startsWith((SEC "_"), Qt::CaseInsensitive))
#define FIRST_TOK(STR) (STR.split(" ", QString::SkipEmptyParts).first()) #define FIRST_TOK(STR) (STR.split(" ", QString::SkipEmptyParts).first())
/* static vars */
QMutex AnalyzeTask::s_waitMutex;
QWaitCondition AnalyzeTask::s_waitCond;
QSet<unsigned int> AnalyzeTask::s_threadIdx_running;
unsigned int AnalyzeTask::s_threadIdx_next = 0;
QSemaphore AnalyzeTask::s_semaphore(0);
/* more static vars */
QReadWriteLock AnalyzeTask::s_lock;
unsigned int AnalyzeTask::s_filesAccepted = 0;
unsigned int AnalyzeTask::s_filesRejected = 0;
unsigned int AnalyzeTask::s_filesDenied = 0;
unsigned int AnalyzeTask::s_filesDummyCDDA = 0;
unsigned int AnalyzeTask::s_filesCueSheet = 0;
QStringList AnalyzeTask::s_additionalFiles;
QSet<QString> AnalyzeTask::s_recentlyAdded;
/*constants*/
const int WAITCOND_TIMEOUT = 2500;
const int MAX_RETRIES = 60000 / WAITCOND_TIMEOUT;
const int MAX_QUEUE_SLOTS = 32;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Constructor // Constructor
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
AnalyzeTask::AnalyzeTask(const QString &inputFile, const QString &templateFile, volatile bool *abortFlag) AnalyzeTask::AnalyzeTask(const int taskId, const QString &inputFile, const QString &templateFile, volatile bool *abortFlag)
: :
m_threadIdx(makeThreadIdx()), m_taskId(taskId),
m_inputFile(inputFile), m_inputFile(inputFile),
m_templateFile(templateFile), m_templateFile(templateFile),
m_mediaInfoBin(lamexp_lookup_tool("mediainfo.exe")), m_mediaInfoBin(lamexp_lookup_tool("mediainfo.exe")),
@ -88,13 +65,7 @@ AnalyzeTask::AnalyzeTask(const QString &inputFile, const QString &templateFile,
AnalyzeTask::~AnalyzeTask(void) AnalyzeTask::~AnalyzeTask(void)
{ {
s_semaphore.release(); emit taskCompleted(m_taskId);
s_waitMutex.lock();
s_threadIdx_running.remove(m_threadIdx);
s_waitMutex.unlock();
s_waitCond.wakeAll();
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -111,12 +82,6 @@ void AnalyzeTask::run()
{ {
qWarning("WARNING: Caught an in exception AnalyzeTask thread!"); qWarning("WARNING: Caught an in exception AnalyzeTask thread!");
} }
s_waitMutex.lock();
s_threadIdx_running.remove(m_threadIdx);
s_waitMutex.unlock();
s_waitCond.wakeAll();
} }
void AnalyzeTask::run_ex(void) void AnalyzeTask::run_ex(void)
@ -125,9 +90,6 @@ void AnalyzeTask::run_ex(void)
QString currentFile = QDir::fromNativeSeparators(m_inputFile); QString currentFile = QDir::fromNativeSeparators(m_inputFile);
qDebug("Analyzing: %s", currentFile.toUtf8().constData()); qDebug("Analyzing: %s", currentFile.toUtf8().constData());
emit fileSelected(QFileInfo(currentFile).fileName());
emit progressValChanged(m_threadIdx + 1);
AudioFileModel file = analyzeFile(currentFile, &fileType); AudioFileModel file = analyzeFile(currentFile, &fileType);
if(*m_abortFlag) if(*m_abortFlag)
@ -135,79 +97,46 @@ void AnalyzeTask::run_ex(void)
qWarning("Operation cancelled by user!"); qWarning("Operation cancelled by user!");
return; return;
} }
if(fileType == fileTypeSkip)
{
qWarning("File was recently added, skipping!");
return;
}
if(fileType == fileTypeDenied)
{
QWriteLocker lock(&s_lock);
s_filesDenied++;
lock.unlock();
qWarning("Cannot access file for reading, skipping!");
return;
}
if(fileType == fileTypeCDDA)
{
QWriteLocker lock(&s_lock);
s_filesDummyCDDA++;
lock.unlock();
qWarning("Dummy CDDA file detected, skipping!");
return;
}
//Handle files with *incomplete* meida info switch(fileType)
if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
{ {
QStringList fileList; case fileTypeDenied:
if(PlaylistImporter::importPlaylist(fileList, currentFile)) qWarning("Cannot access file for reading, skipping!");
break;
case fileTypeCDDA:
qWarning("Dummy CDDA file detected, skipping!");
break;
default:
if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
{ {
qDebug("Imported playlist file."); fileType = fileTypeUnknown;
QWriteLocker lock(&s_lock); if(!QFileInfo(currentFile).suffix().compare("cue", Qt::CaseInsensitive))
s_additionalFiles << fileList;
}
else if(!QFileInfo(currentFile).suffix().compare("cue", Qt::CaseInsensitive))
{
QWriteLocker lock(&s_lock);
qWarning("Cue Sheet file detected, skipping!");
s_filesCueSheet++;
}
else if(!QFileInfo(currentFile).suffix().compare("avs", Qt::CaseInsensitive))
{
qDebug("Found a potential Avisynth script, investigating...");
if(analyzeAvisynthFile(currentFile, file))
{ {
QWriteLocker lock(&s_lock); qWarning("Cue Sheet file detected, skipping!");
s_filesAccepted++; fileType = fileTypeCueSheet;
s_recentlyAdded.insert(file.filePath().toLower()); }
lock.unlock(); else if(!QFileInfo(currentFile).suffix().compare("avs", Qt::CaseInsensitive))
waitForPreviousThreads(); {
emit fileAnalyzed(file); qDebug("Found a potential Avisynth script, investigating...");
if(analyzeAvisynthFile(currentFile, file))
{
fileType = fileTypeNormal;
}
else
{
qDebug("Rejected Avisynth file: %s", file.filePath().toUtf8().constData());
}
} }
else else
{ {
QWriteLocker lock(&s_lock); qDebug("Rejected file of unknown type: %s", file.filePath().toUtf8().constData());
qDebug("Rejected Avisynth file: %s", file.filePath().toUtf8().constData());
s_filesRejected++;
} }
} }
else break;
{
QWriteLocker lock(&s_lock);
qDebug("Rejected file of unknown type: %s", file.filePath().toUtf8().constData());
s_filesRejected++;
}
return;
} }
//Emit the file now! //Emit the file now!
QWriteLocker lock(&s_lock); emit fileAnalyzed(m_taskId, fileType, file);
s_filesAccepted++;
s_recentlyAdded.insert(file.filePath().toLower());
lock.unlock();
waitForPreviousThreads();
emit fileAnalyzed(file);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -219,14 +148,6 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type
*type = fileTypeNormal; *type = fileTypeNormal;
AudioFileModel audioFile(filePath); AudioFileModel audioFile(filePath);
QReadLocker readLock(&s_lock);
if(s_recentlyAdded.contains(filePath.toLower()))
{
*type = fileTypeSkip;
return audioFile;
}
readLock.unlock();
QFile readTest(filePath); QFile readTest(filePath);
if(!readTest.open(QIODevice::ReadOnly)) if(!readTest.open(QIODevice::ReadOnly))
{ {
@ -733,139 +654,12 @@ unsigned int AnalyzeTask::parseDuration(const QString &str)
return ok ? (value/1000) : 0; return ok ? (value/1000) : 0;
} }
unsigned __int64 AnalyzeTask::makeThreadIdx(void)
{
s_waitMutex.lock();
unsigned int idx = s_threadIdx_next++;
s_threadIdx_running.insert(idx);
s_waitMutex.unlock();
return idx;
}
void AnalyzeTask::waitForPreviousThreads(void)
{
//This function will block until all threads with a *lower* index have terminated.
//Required to make sure that the files will be added in the "correct" order!
s_waitMutex.lock();
int retryCount = 0;
forever
{
bool bWaitFlag = false;
QSet<unsigned int>::const_iterator i;
for(i = s_threadIdx_running.begin(); i != s_threadIdx_running.end(); ++i)
{
if(*i < m_threadIdx) { bWaitFlag = true; break; }
}
if((!bWaitFlag) || *m_abortFlag)
{
s_waitMutex.unlock();
return;
}
if(!s_waitCond.wait(&s_waitMutex, WAITCOND_TIMEOUT))
{
if(++retryCount > MAX_RETRIES)
{
qWarning("AnalyzeTask::waitForPreviousThreads encountered timeout !!!");
s_threadIdx_running.clear();
}
}
}
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Public Functions // Public Functions
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
unsigned int AnalyzeTask::filesAccepted(void) /*NONE*/
{
QReadLocker lock(&s_lock);
return s_filesAccepted;
}
unsigned int AnalyzeTask::filesRejected(void)
{
QReadLocker lock(&s_lock);
return s_filesRejected;
}
unsigned int AnalyzeTask::filesDenied(void)
{
QReadLocker lock(&s_lock);
return s_filesDenied;
}
unsigned int AnalyzeTask::filesDummyCDDA(void)
{
QReadLocker lock(&s_lock);
return s_filesDummyCDDA;
}
unsigned int AnalyzeTask::filesCueSheet(void)
{
QReadLocker lock(&s_lock);
return s_filesCueSheet;
}
int AnalyzeTask::getAdditionalFiles(QStringList &fileList)
{
QReadLocker readLock(&s_lock);
int count = s_additionalFiles.count();
readLock.unlock();
if(count > 0)
{
QWriteLocker lock(&s_lock);
count = s_additionalFiles.count();
fileList << s_additionalFiles;
s_additionalFiles.clear();
return count;
}
return 0;
}
bool AnalyzeTask::waitForFreeSlot(volatile bool *abortFlag)
{
bool ret = false;
for(int i = 0; i < MAX_RETRIES; i++)
{
ret = s_semaphore.tryAcquire(1, WAITCOND_TIMEOUT);
if(ret || (*abortFlag)) break;
}
return ret;
}
void AnalyzeTask::reset(void)
{
QWriteLocker lock(&s_lock);
s_filesAccepted = 0;
s_filesRejected = 0;
s_filesDenied = 0;
s_filesDummyCDDA = 0;
s_filesCueSheet = 0;
s_additionalFiles.clear();
s_recentlyAdded.clear();
lock.unlock();
s_waitMutex.lock();
s_threadIdx_next = 0;
s_threadIdx_running.clear();
s_waitMutex.unlock();
int freeSlots = s_semaphore.available();
if(freeSlots < MAX_QUEUE_SLOTS)
{
s_semaphore.release(MAX_QUEUE_SLOTS - freeSlots);
}
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// EVENTS // EVENTS

View File

@ -43,25 +43,24 @@ class AnalyzeTask: public QObject, public QRunnable
Q_OBJECT Q_OBJECT
public: public:
AnalyzeTask(const QString &inputFile, const QString &templateFile, volatile bool *abortFlag); AnalyzeTask(const int taskId, const QString &inputFile, const QString &templateFile, volatile bool *abortFlag);
~AnalyzeTask(void); ~AnalyzeTask(void);
static void reset(void); enum fileType_t
static int getAdditionalFiles(QStringList &fileList); {
static unsigned int filesAccepted(void); fileTypeNormal = 0,
static unsigned int filesRejected(void); fileTypeCDDA = 1,
static unsigned int filesDenied(void); fileTypeDenied = 2,
static unsigned int filesDummyCDDA(void); fileTypeCueSheet = 3,
static unsigned int filesCueSheet(void); fileTypeUnknown = 4
};
//Wait until there is a free slot in the queue //Wait until there is a free slot in the queue
static bool waitForFreeSlot(volatile bool *abortFlag); static bool waitForFreeSlot(volatile bool *abortFlag);
signals: signals:
void fileSelected(const QString &fileName); void fileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file);
void fileAnalyzed(const AudioFileModel &file); void taskCompleted(const unsigned int taskId);
void progressValChanged(unsigned int);
void progressMaxChanged(unsigned int);
protected: protected:
void run(void); void run(void);
@ -75,13 +74,6 @@ private:
coverPng, coverPng,
coverGif coverGif
}; };
enum fileType_t
{
fileTypeNormal = 0,
fileTypeCDDA = 1,
fileTypeDenied = 2,
fileTypeSkip = 3
};
const AudioFileModel analyzeFile(const QString &filePath, int *type); const AudioFileModel analyzeFile(const QString &filePath, int *type);
void updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned int *id_val, cover_t *coverType, QByteArray *coverData, const QString &key, const QString &value); void updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned int *id_val, cover_t *coverType, QByteArray *coverData, const QString &key, const QString &value);
@ -92,7 +84,7 @@ private:
bool analyzeAvisynthFile(const QString &filePath, AudioFileModel &info); bool analyzeAvisynthFile(const QString &filePath, AudioFileModel &info);
void waitForPreviousThreads(void); void waitForPreviousThreads(void);
const unsigned __int64 m_threadIdx; const unsigned int m_taskId;
const QString m_mediaInfoBin; const QString m_mediaInfoBin;
const QString m_avs2wavBin; const QString m_avs2wavBin;
@ -100,22 +92,4 @@ private:
const QString m_inputFile; const QString m_inputFile;
volatile bool *m_abortFlag; volatile bool *m_abortFlag;
static QMutex s_waitMutex;
static QWaitCondition s_waitCond;
static QSet<unsigned int> s_threadIdx_running;
static unsigned int s_threadIdx_next;
static QSemaphore s_semaphore;
static QReadWriteLock s_lock;
static unsigned int s_filesAccepted;
static unsigned int s_filesRejected;
static unsigned int s_filesDenied;
static unsigned int s_filesDummyCDDA;
static unsigned int s_filesCueSheet;
static QSet<QString> s_recentlyAdded;
static QStringList s_additionalFiles;
static unsigned __int64 makeThreadIdx(void);
}; };