Properly detect Windows 8, now that Qt supports it officially.
[LameXP.git] / src / Thread_FileAnalyzer.cpp
blob4ac03b24d70e6f179e43299ae4c9d4d85ac4d778
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "Thread_FileAnalyzer.h"
24 #include "Global.h"
25 #include "LockedFile.h"
26 #include "Model_AudioFile.h"
27 #include "Thread_FileAnalyzer_Task.h"
28 #include "PlaylistImporter.h"
30 #include <QDir>
31 #include <QFileInfo>
32 #include <QProcess>
33 #include <QDate>
34 #include <QTime>
35 #include <QDebug>
36 #include <QImage>
37 #include <QThreadPool>
38 #include <QTime>
39 #include <QElapsedTimer>
40 #include <QTimer>
42 ////////////////////////////////////////////////////////////
43 // Constructor
44 ////////////////////////////////////////////////////////////
46 FileAnalyzer::FileAnalyzer(const QStringList &inputFiles)
48 m_tasksCounterNext(0),
49 m_tasksCounterDone(0),
50 m_inputFiles(inputFiles),
51 m_templateFile(NULL),
52 m_pool(NULL)
54 m_bSuccess = false;
55 m_bAborted = false;
57 m_filesAccepted = 0;
58 m_filesRejected = 0;
59 m_filesDenied = 0;
60 m_filesDummyCDDA = 0;
61 m_filesCueSheet = 0;
63 m_timer = new QElapsedTimer;
66 FileAnalyzer::~FileAnalyzer(void)
68 if(m_templateFile)
70 QString templatePath = m_templateFile->filePath();
71 LAMEXP_DELETE(m_templateFile);
72 if(QFile::exists(templatePath)) QFile::remove(templatePath);
75 if(!m_pool->waitForDone(2500))
77 qWarning("There are still running tasks in the thread pool!");
80 LAMEXP_DELETE(m_pool);
81 LAMEXP_DELETE(m_timer);
84 ////////////////////////////////////////////////////////////
85 // Static data
86 ////////////////////////////////////////////////////////////
88 const char *FileAnalyzer::g_tags_gen[] =
90 "ID",
91 "Format",
92 "Format_Profile",
93 "Format_Version",
94 "Duration",
95 "Title", "Track",
96 "Track/Position",
97 "Artist", "Performer",
98 "Album",
99 "Genre",
100 "Released_Date", "Recorded_Date",
101 "Comment",
102 "Cover",
103 "Cover_Type",
104 "Cover_Mime",
105 "Cover_Data",
106 NULL
109 const char *FileAnalyzer::g_tags_aud[] =
111 "ID",
112 "Source",
113 "Format",
114 "Format_Profile",
115 "Format_Version",
116 "Channel(s)",
117 "SamplingRate",
118 "BitDepth",
119 "BitRate",
120 "BitRate_Mode",
121 "Encoded_Library",
122 NULL
125 ////////////////////////////////////////////////////////////
126 // Thread Main
127 ////////////////////////////////////////////////////////////
129 void FileAnalyzer::run()
131 m_bSuccess = false;
133 m_tasksCounterNext = 0;
134 m_tasksCounterDone = 0;
135 m_completedCounter = 0;
137 m_completedFiles.clear();
138 m_completedTaskIds.clear();
139 m_runningTaskIds.clear();
141 m_filesAccepted = 0;
142 m_filesRejected = 0;
143 m_filesDenied = 0;
144 m_filesDummyCDDA = 0;
145 m_filesCueSheet = 0;
147 m_timer->invalidate();
149 //Update progress
150 emit progressMaxChanged(m_inputFiles.count());
151 emit progressValChanged(0);
153 //Create MediaInfo template file
154 if(!m_templateFile)
156 if(!createTemplate())
158 qWarning("Failed to create template file!");
159 return;
163 //Handle playlist files
164 lamexp_natural_string_sort(m_inputFiles, true);
165 handlePlaylistFiles();
166 lamexp_natural_string_sort(m_inputFiles, true);
168 const unsigned int nFiles = m_inputFiles.count();
170 //Update progress
171 emit progressMaxChanged(nFiles);
173 //Create thread pool
174 if(!m_pool) m_pool = new QThreadPool();
175 const int idealThreadCount = QThread::idealThreadCount();
176 if(idealThreadCount > 0)
178 m_pool->setMaxThreadCount(qBound(2, ((idealThreadCount * 3) / 2), 12));
181 //Start first N threads
182 QTimer::singleShot(0, this, SLOT(initializeTasks()));
184 //Start event processing
185 this->exec();
187 //Wait for pending tasks to complete
188 m_pool->waitForDone();
190 //Was opertaion aborted?
191 if(m_bAborted)
193 qWarning("Operation cancelled by user!");
194 return;
197 //Update progress
198 emit progressValChanged(nFiles);
200 //Emit pending files (this should not be required though!)
201 if(!m_completedFiles.isEmpty())
203 qWarning("FileAnalyzer: Pending file information found after last thread terminated!");
204 QList<unsigned int> keys = m_completedFiles.keys(); qSort(keys);
205 while(!keys.isEmpty())
207 emit fileAnalyzed(m_completedFiles.take(keys.takeFirst()));
211 qDebug("All files added.\n");
212 m_bSuccess = true;
214 QThread::msleep(333);
217 ////////////////////////////////////////////////////////////
218 // Privtae Functions
219 ////////////////////////////////////////////////////////////
221 bool FileAnalyzer::analyzeNextFile(void)
223 if(!(m_inputFiles.isEmpty() || m_bAborted))
225 const unsigned int taskId = m_tasksCounterNext++;
226 const QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
228 if((!m_timer->isValid()) || (m_timer->elapsed() >= 250))
230 emit fileSelected(QFileInfo(currentFile).fileName());
231 m_timer->restart();
234 AnalyzeTask *task = new AnalyzeTask(taskId, currentFile, m_templateFile->filePath(), &m_bAborted);
235 connect(task, SIGNAL(fileAnalyzed(const unsigned int, const int, AudioFileModel)), this, SLOT(taskFileAnalyzed(unsigned int, const int, AudioFileModel)), Qt::QueuedConnection);
236 connect(task, SIGNAL(taskCompleted(const unsigned int)), this, SLOT(taskThreadFinish(const unsigned int)), Qt::QueuedConnection);
237 m_runningTaskIds.insert(taskId); m_pool->start(task);
239 return true;
242 return false;
245 void FileAnalyzer::handlePlaylistFiles(void)
247 QStringList importedFiles;
248 while(!m_inputFiles.isEmpty())
250 const QString currentFile = m_inputFiles.takeFirst();
251 if(!PlaylistImporter::importPlaylist(importedFiles, currentFile))
253 importedFiles << currentFile;
257 while(!importedFiles.isEmpty())
259 const QString currentFile = importedFiles.takeFirst();
260 if(!m_inputFiles.contains(currentFile, Qt::CaseInsensitive))
262 m_inputFiles << currentFile;
267 bool FileAnalyzer::createTemplate(void)
269 if(m_templateFile)
271 qWarning("Template file already exists!");
272 return true;
275 QString templatePath = QString("%1/%2.txt").arg(lamexp_temp_folder2(), lamexp_rand_str());
277 QFile templateFile(templatePath);
278 if(!templateFile.open(QIODevice::WriteOnly))
280 return false;
283 templateFile.write("General;");
284 for(size_t i = 0; g_tags_gen[i]; i++)
286 templateFile.write(QString("Gen_%1=%%1%\\n").arg(g_tags_gen[i]).toLatin1().constData());
288 templateFile.write("\\n\r\n");
290 templateFile.write("Audio;");
291 for(size_t i = 0; g_tags_aud[i]; i++)
293 templateFile.write(QString("Aud_%1=%%1%\\n").arg(g_tags_aud[i]).toLatin1().constData());
295 templateFile.write("\\n\r\n");
297 bool success = (templateFile.error() == QFile::NoError);
298 templateFile.close();
300 if(!success)
302 QFile::remove(templatePath);
303 return false;
308 m_templateFile = new LockedFile(templatePath);
310 catch(...)
312 qWarning("Failed to lock template file!");
313 return false;
316 return true;
319 ////////////////////////////////////////////////////////////
320 // Slot Functions
321 ////////////////////////////////////////////////////////////
323 void FileAnalyzer::initializeTasks(void)
325 for(int i = 0; i < m_pool->maxThreadCount(); i++)
327 if(!analyzeNextFile()) break;
331 void FileAnalyzer::taskFileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file)
333 m_completedTaskIds.insert(taskId);
335 switch(fileType)
337 case AnalyzeTask::fileTypeNormal:
338 m_filesAccepted++;
339 if(m_tasksCounterDone == taskId)
341 emit fileAnalyzed(file);
342 m_tasksCounterDone++;
344 else
346 m_completedFiles.insert(taskId, file);
348 break;
349 case AnalyzeTask::fileTypeCDDA:
350 m_filesDummyCDDA++;
351 break;
352 case AnalyzeTask::fileTypeDenied:
353 m_filesDenied++;
354 break;
355 case AnalyzeTask::fileTypeCueSheet:
356 m_filesCueSheet++;
357 break;
358 case AnalyzeTask::fileTypeUnknown:
359 m_filesRejected++;
360 break;
361 default:
362 throw "Unknown file type identifier!";
365 //Emit all pending files
366 while(m_completedTaskIds.contains(m_tasksCounterDone))
368 if(m_completedFiles.contains(m_tasksCounterDone))
370 emit fileAnalyzed(m_completedFiles.take(m_tasksCounterDone));
372 m_completedTaskIds.remove(m_tasksCounterDone);
373 m_tasksCounterDone++;
377 void FileAnalyzer::taskThreadFinish(const unsigned int taskId)
379 m_runningTaskIds.remove(taskId);
380 emit progressValChanged(++m_completedCounter);
382 if(!analyzeNextFile())
384 if(m_runningTaskIds.empty())
386 QTimer::singleShot(0, this, SLOT(quit())); //Stop event processing, if all threads have completed!
391 ////////////////////////////////////////////////////////////
392 // Public Functions
393 ////////////////////////////////////////////////////////////
395 unsigned int FileAnalyzer::filesAccepted(void)
397 return m_filesAccepted;
400 unsigned int FileAnalyzer::filesRejected(void)
402 return m_filesRejected;
405 unsigned int FileAnalyzer::filesDenied(void)
407 return m_filesDenied;
410 unsigned int FileAnalyzer::filesDummyCDDA(void)
412 return m_filesDummyCDDA;
415 unsigned int FileAnalyzer::filesCueSheet(void)
417 return m_filesCueSheet;
420 ////////////////////////////////////////////////////////////
421 // EVENTS
422 ////////////////////////////////////////////////////////////
424 /*NONE*/