OSDN Git Service

Simplified QKeccakHash and added self-test function.
[lamexp/LameXP.git] / src / Thread_FileAnalyzer.cpp
index d26279d..f9cd516 100644 (file)
@@ -1,6 +1,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // LameXP - Audio Encoder Front-End
-// Copyright (C) 2004-2010 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
 #include "Global.h"
 #include "LockedFile.h"
 #include "Model_AudioFile.h"
+#include "Thread_FileAnalyzer_Task.h"
 
 #include <QDir>
 #include <QFileInfo>
 #include <QProcess>
 #include <QDate>
 #include <QTime>
-
-#include <math.h>
+#include <QDebug>
+#include <QImage>
+#include <QThreadPool>
+#include <QTime>
 
 ////////////////////////////////////////////////////////////
 // Constructor
 ////////////////////////////////////////////////////////////
 
 FileAnalyzer::FileAnalyzer(const QStringList &inputFiles)
-       : m_inputFiles(inputFiles)
+:
+       m_abortFlag(false),
+       m_inputFiles(inputFiles),
+       m_templateFile(NULL)
 {
        m_bSuccess = false;
-       m_mediaInfoBin = lamexp_lookup_tool("mediainfo_icl11.exe");
-       
-       if(m_mediaInfoBin.isEmpty())
+       m_bAborted = false;
+}
+
+FileAnalyzer::~FileAnalyzer(void)
+{
+       if(m_templateFile)
        {
-               qFatal("Invalid path to MediaInfo binary. Tool not initialized properly.");
+               QString templatePath = m_templateFile->filePath();
+               LAMEXP_DELETE(m_templateFile);
+               if(QFile::exists(templatePath)) QFile::remove(templatePath);
        }
+       
+       AnalyzeTask::reset();
 }
 
 ////////////////////////////////////////////////////////////
+// Static data
+////////////////////////////////////////////////////////////
+
+const char *FileAnalyzer::g_tags_gen[] =
+{
+       "ID",
+       "Format",
+       "Format_Profile",
+       "Format_Version",
+       "Duration",
+       "Title", "Track",
+       "Track/Position",
+       "Artist", "Performer",
+       "Album",
+       "Genre",
+       "Released_Date", "Recorded_Date",
+       "Comment",
+       "Cover",
+       "Cover_Type",
+       "Cover_Mime",
+       "Cover_Data",
+       NULL
+};
+
+const char *FileAnalyzer::g_tags_aud[] =
+{
+       "ID",
+       "Source",
+       "Format",
+       "Format_Profile",
+       "Format_Version",
+       "Channel(s)",
+       "SamplingRate",
+       "BitDepth",
+       "BitRate",
+       "BitRate_Mode",
+       "Encoded_Library",
+       NULL
+};
+
+////////////////////////////////////////////////////////////
 // Thread Main
 ////////////////////////////////////////////////////////////
 
 void FileAnalyzer::run()
 {
+       m_abortFlag = false;
+
+       m_bAborted = false;
        m_bSuccess = false;
+
+       int nFiles = m_inputFiles.count();
+
+       emit progressMaxChanged(nFiles);
+       emit progressValChanged(0);
+
        m_inputFiles.sort();
 
-       while(!m_inputFiles.isEmpty())
+       if(!m_templateFile)
        {
-               QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
-               qDebug("Analyzing: %s", currentFile.toUtf8().constData());
-               emit fileSelected(QFileInfo(currentFile).fileName());
-               AudioFileModel file = analyzeFile(currentFile);
-               if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
+               if(!createTemplate())
                {
-                       qDebug("Skipped: %s", file.filePath().toUtf8().constData());
-                       continue;
+                       qWarning("Failed to create template file!");
+                       return;
                }
-               emit fileAnalyzed(file);
        }
 
-       qDebug("All files added.\n");
-       m_bSuccess = true;
-}
-
-////////////////////////////////////////////////////////////
-// Public Functions
-////////////////////////////////////////////////////////////
-
-const AudioFileModel FileAnalyzer::analyzeFile(const QString &filePath)
-{
-       AudioFileModel audioFile(filePath);
-       m_currentSection = sectionOther;
-
-       QFile readTest(filePath);
-       if(!readTest.open(QIODevice::ReadOnly))
-       {
-               qWarning("Cannot access file for reading, skipping!");
-               return audioFile;
-       }
-       else
-       {
-               readTest.close();
-       }
+       AnalyzeTask::reset();
+       QThreadPool *pool = new QThreadPool();
+       QThread::msleep(333);
 
-       QProcess process;
-       process.setProcessChannelMode(QProcess::MergedChannels);
-       process.setReadChannel(QProcess::StandardOutput);
-       process.start(m_mediaInfoBin, QStringList() << QDir::toNativeSeparators(filePath));
-       process.waitForStarted();
+       pool->setMaxThreadCount(qBound(2, ((QThread::idealThreadCount() * 3) / 2), 12));
 
-       while(process.state() != QProcess::NotRunning)
+       while(!(m_inputFiles.isEmpty() || m_bAborted))
        {
-               if(!process.waitForReadyRead())
+               while(!(m_inputFiles.isEmpty() || m_bAborted))
                {
-                       if(process.state() == QProcess::Running)
+                       if(!AnalyzeTask::waitForFreeSlot(&m_abortFlag))
                        {
-                               qWarning("MediaInfo time out. Killing process and skipping file!");
-                               process.kill();
-                               process.waitForFinished(-1);
-                               return audioFile;
+                               qWarning("Timeout in AnalyzeTask::waitForFreeSlot() !!!");
                        }
-               }
 
-               QByteArray data = process.readLine().constData();
-               while(data.size() > 0)
-               {
-                       QString line = QString::fromUtf8(data).simplified();
-                       if(!line.isEmpty())
+                       if(m_abortFlag)
                        {
-                               int index = line.indexOf(':');
-                               if(index > 0)
-                               {
-                                       QString key = line.left(index-1).trimmed();
-                                       QString val = line.mid(index+1).trimmed();
-                                       if(!key.isEmpty() && !val.isEmpty())
-                                       {
-                                               updateInfo(audioFile, key, val);
-                                       }
-                               }
-                               else
-                               {
-                                       updateSection(line);
-                               }
+                               MessageBeep(MB_ICONERROR);
+                               m_bAborted = true;
+                               break;
                        }
-                       data = process.readLine().constData();
-               }
-       }
+                       
+                       if(!m_bAborted)
+                       {
+                               const QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
 
-       if(audioFile.fileName().isEmpty())
-       {
-               QString baseName = QFileInfo(filePath).fileName();
-               int index = baseName.lastIndexOf(".");
+                               AnalyzeTask *task = new AnalyzeTask(currentFile, m_templateFile->filePath(), &m_abortFlag);
+                               connect(task, SIGNAL(fileSelected(QString)), this, SIGNAL(fileSelected(QString)), Qt::DirectConnection);
+                               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);
 
-               if(index >= 0)
-               {
-                       baseName = baseName.left(index);
-               }
+                               pool->start(task);
 
-               baseName = baseName.replace("_", " ").simplified();
-               index = baseName.lastIndexOf(" - ");
+                               if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
+                               {
+                                       emit progressMaxChanged(nFiles += count);
+                               }
+                       }
+               }
 
-               if(index >= 0)
+               //One of the Analyze tasks may have gathered additional files from a playlist!
+               if(!m_bAborted)
                {
-                       baseName = baseName.mid(index + 3).trimmed();
+                       pool->waitForDone();
+                       if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
+                       {
+                               emit progressMaxChanged(nFiles += count);
+                       }
                }
+       }
+       
+       pool->waitForDone();
+       LAMEXP_DELETE(pool);
 
-               audioFile.setFileName(baseName);
+       if(m_bAborted)
+       {
+               qWarning("Operation cancelled by user!");
+               return;
        }
        
-       return audioFile;
+       qDebug("All files added.\n");
+       m_bSuccess = true;
 }
 
-void FileAnalyzer::updateSection(const QString &section)
+////////////////////////////////////////////////////////////
+// Privtae Functions
+////////////////////////////////////////////////////////////
+
+bool FileAnalyzer::createTemplate(void)
 {
-       if(section.startsWith("General", Qt::CaseInsensitive))
+       if(m_templateFile)
        {
-               m_currentSection = sectionGeneral;
+               qWarning("Template file already exists!");
+               return true;
        }
-       else if(!section.compare("Audio", Qt::CaseInsensitive) || section.startsWith("Audio #1", Qt::CaseInsensitive))
+       
+       QString templatePath = QString("%1/%2.txt").arg(lamexp_temp_folder2(), lamexp_rand_str());
+
+       QFile templateFile(templatePath);
+       if(!templateFile.open(QIODevice::WriteOnly))
        {
-               m_currentSection = sectionAudio;
+               return false;
        }
-       else if(section.startsWith("Audio", Qt::CaseInsensitive) || section.startsWith("Video", Qt::CaseInsensitive) || section.startsWith("Text", Qt::CaseInsensitive) ||
-               section.startsWith("Menu", Qt::CaseInsensitive) || section.startsWith("Image", Qt::CaseInsensitive) || section.startsWith("Chapters", Qt::CaseInsensitive))
+
+       templateFile.write("General;");
+       for(size_t i = 0; g_tags_gen[i]; i++)
        {
-               m_currentSection = sectionOther;
+               templateFile.write(QString("Gen_%1=%%1%\\n").arg(g_tags_gen[i]).toLatin1().constData());
        }
-       else
+       templateFile.write("\\n\r\n");
+
+       templateFile.write("Audio;");
+       for(size_t i = 0; g_tags_aud[i]; i++)
        {
-               qWarning("Unknown section: %s", section.toUtf8().constData());
+               templateFile.write(QString("Aud_%1=%%1%\\n").arg(g_tags_aud[i]).toLatin1().constData());
        }
-}
+       templateFile.write("\\n\r\n");
 
-void FileAnalyzer::updateInfo(AudioFileModel &audioFile, const QString &key, const QString &value)
-{
-       switch(m_currentSection)
+       bool success = (templateFile.error() == QFile::NoError);
+       templateFile.close();
+       
+       if(!success)
        {
-       case sectionGeneral:
-               if(!key.compare("Title", Qt::CaseInsensitive) || !key.compare("Track", Qt::CaseInsensitive) || !key.compare("Track Name", Qt::CaseInsensitive))
-               {
-                       if(audioFile.fileName().isEmpty()) audioFile.setFileName(value);
-               }
-               else if(!key.compare("Duration", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.fileDuration()) audioFile.setFileDuration(parseDuration(value));
-               }
-               else if(!key.compare("Artist", Qt::CaseInsensitive) || !key.compare("Performer", Qt::CaseInsensitive))
-               {
-                       if(audioFile.fileArtist().isEmpty()) audioFile.setFileArtist(value);
-               }
-               else if(!key.compare("Album", Qt::CaseInsensitive))
-               {
-                       if(audioFile.fileAlbum().isEmpty()) audioFile.setFileAlbum(value);
-               }
-               else if(!key.compare("Genre", Qt::CaseInsensitive))
-               {
-                       if(audioFile.fileGenre().isEmpty()) audioFile.setFileGenre(value);
-               }
-               else if(!key.compare("Year", Qt::CaseInsensitive) || !key.compare("Recorded Date", Qt::CaseInsensitive) || !key.compare("Encoded Date", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.fileYear()) audioFile.setFileYear(parseYear(value));
-               }
-               else if(!key.compare("Comment", Qt::CaseInsensitive))
-               {
-                       if(audioFile.fileComment().isEmpty()) audioFile.setFileComment(value);
-               }
-               else if(!key.compare("Track Name/Position", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.filePosition()) audioFile.setFilePosition(value.toInt());
-               }
-               else if(!key.compare("Format", Qt::CaseInsensitive))
-               {
-                       if(audioFile.formatContainerType().isEmpty()) audioFile.setFormatContainerType(value);
-               }
-               else if(!key.compare("Format Profile", Qt::CaseInsensitive))
-               {
-                       if(audioFile.formatContainerProfile().isEmpty()) audioFile.setFormatContainerProfile(value);
-               }
-               break;
-
-       case sectionAudio:
-               if(!key.compare("Year", Qt::CaseInsensitive) || !key.compare("Recorded Date", Qt::CaseInsensitive) || !key.compare("Encoded Date", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.fileYear()) audioFile.setFileYear(parseYear(value));
-               }
-               else if(!key.compare("Format", Qt::CaseInsensitive))
-               {
-                       if(audioFile.formatAudioType().isEmpty()) audioFile.setFormatAudioType(value);
-               }
-               else if(!key.compare("Format Profile", Qt::CaseInsensitive))
-               {
-                       if(audioFile.formatAudioProfile().isEmpty()) audioFile.setFormatAudioProfile(value);
-               }
-               else if(!key.compare("Format Version", Qt::CaseInsensitive))
-               {
-                       if(audioFile.formatAudioVersion().isEmpty()) audioFile.setFormatAudioVersion(value);
-               }
-               else if(!key.compare("Channel(s)", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.formatAudioChannels()) audioFile.setFormatAudioChannels(value.split(" ", QString::SkipEmptyParts).first().toInt());
-               }
-               else if(!key.compare("Sampling rate", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.formatAudioSamplerate()) audioFile.setFormatAudioSamplerate(ceil(value.split(" ", QString::SkipEmptyParts).first().toFloat() * 1000.0f));
-               }
-               else if(!key.compare("Bit depth", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.formatAudioBitdepth()) audioFile.setFormatAudioBitdepth(value.split(" ", QString::SkipEmptyParts).first().toInt());
-               }
-               else if(!key.compare("Duration", Qt::CaseInsensitive))
-               {
-                       if(!audioFile.fileDuration()) audioFile.setFileDuration(parseDuration(value));
-               }
-               break;
+               QFile::remove(templatePath);
+               return false;
        }
-}
 
-unsigned int FileAnalyzer::parseYear(const QString &str)
-{
-       if(str.startsWith("UTC", Qt::CaseInsensitive))
+       try
        {
-               QDate date = QDate::fromString(str.mid(3).trimmed().left(10), "yyyy-MM-dd");
-               if(date.isValid())
-               {
-                       return date.year();
-               }
-               else
-               {
-                       return 0;
-               }
+               m_templateFile = new LockedFile(templatePath);
        }
-       else
+       catch(...)
        {
-               bool ok = false;
-               int year = str.toInt(&ok);
-               if(ok && year > 0)
-               {
-                       return year;
-               }
-               else
-               {
-                       return 0;
-               }
+               qWarning("Failed to lock template file!");
+               return false;
        }
+
+       return true;
 }
 
-unsigned int FileAnalyzer::parseDuration(const QString &str)
-{
-       QTime time;
+////////////////////////////////////////////////////////////
+// Public Functions
+////////////////////////////////////////////////////////////
 
-       time = QTime::fromString(str, "s's 'z'ms'");
-       if(time.isValid())
-       {
-               return (time.hour() * 60 * 60) + (time.minute() * 60) + time.second();
-       }
+unsigned int FileAnalyzer::filesAccepted(void)
+{
+       return AnalyzeTask::filesAccepted();
+}
 
-       time = QTime::fromString(str, "m'mn 's's'");
-       if(time.isValid())
-       {
-               return (time.hour() * 60 * 60) + (time.minute() * 60) + time.second();
-       }
+unsigned int FileAnalyzer::filesRejected(void)
+{
+       return AnalyzeTask::filesRejected();
+}
 
-       time = QTime::fromString(str, "h'h 'm'mn'");
-       if(time.isValid())
-       {
-               return (time.hour() * 60 * 60) + (time.minute() * 60) + time.second();
-       }
+unsigned int FileAnalyzer::filesDenied(void)
+{
+       return AnalyzeTask::filesDenied();
+}
 
-       return 0;
+unsigned int FileAnalyzer::filesDummyCDDA(void)
+{
+       return AnalyzeTask::filesDummyCDDA();
 }
 
+unsigned int FileAnalyzer::filesCueSheet(void)
+{
+       return AnalyzeTask::filesCueSheet();
+}
 
 ////////////////////////////////////////////////////////////
 // EVENTS