diff --git a/src/Config.h b/src/Config.h index a88bd026..86ffafc3 100644 --- a/src/Config.h +++ b/src/Config.h @@ -25,7 +25,7 @@ #define VER_LAMEXP_MAJOR 4 #define VER_LAMEXP_MINOR_HI 0 #define VER_LAMEXP_MINOR_LO 0 -#define VER_LAMEXP_BUILD 54 +#define VER_LAMEXP_BUILD 57 #define VER_LAMEXP_SUFFIX TechPreview /* diff --git a/src/Dialog_MainWindow.cpp b/src/Dialog_MainWindow.cpp index 354e16be..55bb4c70 100644 --- a/src/Dialog_MainWindow.cpp +++ b/src/Dialog_MainWindow.cpp @@ -58,14 +58,12 @@ #include //Helper macros -#define ABORT_IF_BUSY \ -if(m_banner->isVisible() || m_delayedFileTimer->isActive()) \ -{ \ - MessageBeep(MB_ICONEXCLAMATION); \ - return; \ -} +#define ABORT_IF_BUSY if(m_banner->isVisible() || m_delayedFileTimer->isActive()) { MessageBeep(MB_ICONEXCLAMATION); return; } +#define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, COLOR); WIDGET->setPalette(_palette); } +#define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); } #define LINK(URL) QString("%2").arg(URL).arg(URL) + //Helper class class Index: public QObjectUserData { @@ -86,7 +84,8 @@ MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, S m_fileListModel(fileListModel), m_metaData(metaInfo), m_settings(settingsModel), - m_accepted(false) + m_accepted(false), + m_firstTimeShown(true) { //Init the dialog, from the .ui file setupUi(this); @@ -117,6 +116,8 @@ MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, S m_dropNoteLabel = new QLabel(sourceFileView); m_dropNoteLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_dropNoteLabel->setText("» You can drop in audio files here! «"); + SET_FONT_BOLD(m_dropNoteLabel, true); + SET_TEXT_COLOR(m_dropNoteLabel, Qt::darkGray); connect(buttonAddFiles, SIGNAL(clicked()), this, SLOT(addFilesButtonClicked())); connect(buttonRemoveFile, SIGNAL(clicked()), this, SLOT(removeFileButtonClicked())); connect(buttonClearFiles, SIGNAL(clicked()), this, SLOT(clearFilesButtonClicked())); @@ -154,10 +155,12 @@ MainWindow::MainWindow(FileListModel *fileListModel, AudioFileModel *metaInfo, S metaDataView->verticalHeader()->hide(); metaDataView->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); while(writeMetaDataCheckBox->isChecked() != m_settings->writeMetaTags()) writeMetaDataCheckBox->click(); + while(generatePlaylistCheckBox->isChecked() != m_settings->createPlaylist()) generatePlaylistCheckBox->click(); connect(buttonEditMeta, SIGNAL(clicked()), this, SLOT(editMetaButtonClicked())); connect(buttonClearMeta, SIGNAL(clicked()), this, SLOT(clearMetaButtonClicked())); connect(writeMetaDataCheckBox, SIGNAL(clicked()), this, SLOT(metaTagsEnabledChanged())); - + connect(generatePlaylistCheckBox, SIGNAL(clicked()), this, SLOT(playlistEnabledChanged())); + //Setup "Compression" tab m_encoderButtonGroup = new QButtonGroup(this); m_encoderButtonGroup->addButton(radioButtonEncoderMP3, SettingsModel::MP3Encoder); @@ -322,7 +325,13 @@ void MainWindow::showEvent(QShowEvent *event) m_accepted = false; m_dropNoteLabel->setGeometry(0, 0, sourceFileView->width(), sourceFileView->height()); modelReset(); - QTimer::singleShot(0, this, SLOT(windowShown())); + tabWidget->setCurrentIndex(0); + + if(m_firstTimeShown) + { + m_firstTimeShown = false; + QTimer::singleShot(0, this, SLOT(windowShown())); + } } void MainWindow::dragEnterEvent(QDragEnterEvent *event) @@ -1076,3 +1085,11 @@ void MainWindow::metaTagsEnabledChanged(void) { m_settings->writeMetaTags(writeMetaDataCheckBox->isChecked()); } + +/* + * Playlist enabled changed + */ +void MainWindow::playlistEnabledChanged(void) +{ + m_settings->createPlaylist(generatePlaylistCheckBox->isChecked()); +} diff --git a/src/Dialog_MainWindow.h b/src/Dialog_MainWindow.h index 825db175..49a4d84d 100644 --- a/src/Dialog_MainWindow.h +++ b/src/Dialog_MainWindow.h @@ -79,6 +79,7 @@ private slots: void rowsChanged(const QModelIndex &parent, int start, int end); void modelReset(void); void metaTagsEnabledChanged(void); + void playlistEnabledChanged(void); protected: void showEvent(QShowEvent *event); @@ -91,6 +92,7 @@ private: void addFiles(const QStringList &files); bool m_accepted; + bool m_firstTimeShown; FileListModel *m_fileListModel; QFileSystemModel *m_fileSystemModel; QActionGroup *m_tabActionGroup; diff --git a/src/Dialog_Processing.cpp b/src/Dialog_Processing.cpp index c4a3ef0f..6476cb0e 100644 --- a/src/Dialog_Processing.cpp +++ b/src/Dialog_Processing.cpp @@ -37,6 +37,9 @@ #include #include #include +#include +#include + #include //////////////////////////////////////////////////////////// @@ -173,6 +176,7 @@ void ProcessingDialog::initEncoding(void) m_runningThreads = 0; m_currentFile = 0; m_userAborted = false; + m_playList.clear(); label_progress->setText("Encoding files, please wait..."); m_progressIndicator->start(); @@ -224,6 +228,13 @@ void ProcessingDialog::doneEncoding(void) qDebug("Running jobs: %u", m_runningThreads); + if(!m_userAborted && m_settings->createPlaylist() && !m_settings->outputDir().isEmpty()) + { + label_progress->setText("Creatig the play list, please wait..."); + QApplication::processEvents(); + writePlayList(); + } + label_progress->setText(m_userAborted ? "Process was aborted by the user!" : "Alle files completed successfully."); m_progressIndicator->stop(); @@ -234,6 +245,14 @@ void ProcessingDialog::doneEncoding(void) progressBar->setValue(100); } +void ProcessingDialog::processFinished(const QUuid &jobId, const QString &outFileName, bool success) +{ + if(success) + { + m_playList.append(outFileName); + } +} + //////////////////////////////////////////////////////////// // Private Functions //////////////////////////////////////////////////////////// @@ -268,10 +287,43 @@ void ProcessingDialog::startNextJob(void) connect(thread, SIGNAL(finished()), this, SLOT(doneEncoding()), Qt::QueuedConnection); connect(thread, SIGNAL(processStateInitialized(QUuid,QString,QString,int)), m_progressModel, SLOT(addJob(QUuid,QString,QString,int)), Qt::QueuedConnection); connect(thread, SIGNAL(processStateChanged(QUuid,QString,int)), m_progressModel, SLOT(updateJob(QUuid,QString,int)), Qt::QueuedConnection); + connect(thread, SIGNAL(processStateFinished(QUuid,QString,bool)), this, SLOT(processFinished(QUuid,QString,bool)), Qt::QueuedConnection); m_runningThreads++; thread->start(); } +void ProcessingDialog::writePlayList(void) +{ + QString playListName = (m_metaInfo->fileAlbum().isEmpty() ? "Playlist" : m_metaInfo->fileAlbum()); + + const static char *invalidChars = "\\/:*?\"<>|"; + for(int i = 0; invalidChars[i]; i++) + { + playListName.replace(invalidChars[i], ' '); + playListName = playListName.simplified(); + } + + QString playListFile = QString("%1/%2.m3u").arg(m_settings->outputDir(), playListName); + + int counter = 1; + while(QFileInfo(playListFile).exists()) + { + playListFile = QString("%1/%2 (%3).m3u").arg(m_settings->outputDir(), playListName, QString::number(++counter)); + } + + QFile playList(playListFile); + if(playList.open(QIODevice::WriteOnly)) + { + playList.write("#EXTM3U\r\n"); + for(int i = 0; i < m_playList.count(); i++) + { + playList.write(QFileInfo(m_playList.at(i)).fileName().toUtf8().constData()); + playList.write("\r\n"); + } + playList.close(); + } +} + AudioFileModel ProcessingDialog::updateMetaInfo(const AudioFileModel &audioFile) { if(!m_settings->writeMetaTags()) diff --git a/src/Dialog_Processing.h b/src/Dialog_Processing.h index 3a5a736c..fb352320 100644 --- a/src/Dialog_Processing.h +++ b/src/Dialog_Processing.h @@ -23,6 +23,8 @@ #include "../tmp/UIC_ProcessingDialog.h" +#include + class QMovie; class ProgressModel; class ProcessThread; @@ -42,6 +44,7 @@ private slots: void initEncoding(void); void doneEncoding(void); void abortEncoding(void); + void processFinished(const QUuid &jobId, const QString &outFileName, bool success); protected: void showEvent(QShowEvent *event); @@ -52,7 +55,7 @@ private: void setCloseButtonEnabled(bool enabled); void startNextJob(void); AudioFileModel updateMetaInfo(const AudioFileModel &audioFile); - + void writePlayList(void); QList m_pendingJobs; SettingsModel *m_settings; @@ -60,6 +63,7 @@ private: QList m_threadList; QMovie *m_progressIndicator; ProgressModel *m_progressModel; + QStringList m_playList; unsigned int m_runningThreads; unsigned int m_currentFile; bool m_userAborted; diff --git a/src/Main.cpp b/src/Main.cpp index a4b4a717..30c3c8f7 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -52,7 +52,7 @@ int lamexp_main(int argc, char* argv[]) //Print version info qDebug("LameXP - Audio Encoder Front-End"); - qDebug("Version %d.%02d %s, Build %d [%s], MSVC compiler v%02d.%02d", lamexp_version_major(), lamexp_version_minor(), lamexp_version_release(), lamexp_version_build(), lamexp_version_date().toString(Qt::ISODate).toLatin1().constData(), _MSC_VER / 100, _MSC_VER % 100); + qDebug("Version %d.%02d %s, Build %d [%s], compiled with %s", lamexp_version_major(), lamexp_version_minor(), lamexp_version_release(), lamexp_version_build(), lamexp_version_date().toString(Qt::ISODate).toLatin1().constData(), lamexp_version_compiler()); qDebug("Copyright (C) 2004-%04d LoRd_MuldeR \n", max(lamexp_version_date().year(),QDate::currentDate().year())); //print license info @@ -65,10 +65,10 @@ int lamexp_main(int argc, char* argv[]) //Detect CPU capabilities lamexp_cpu_t cpuFeatures = lamexp_detect_cpu_features(); - qDebug("CPU brand string: %s", cpuFeatures.brand); - qDebug("CPU signature: Family: %d, Model: %d, Stepping: %d", cpuFeatures.family, cpuFeatures.model, cpuFeatures.stepping); - qDebug("CPU capabilities: MMX: %s, SSE: %s, SSE2: %s, SSE3: %s, SSSE3: %s, x64: %s", LAMEXP_BOOL(cpuFeatures.mmx), LAMEXP_BOOL(cpuFeatures.sse), LAMEXP_BOOL(cpuFeatures.sse2), LAMEXP_BOOL(cpuFeatures.sse3), LAMEXP_BOOL(cpuFeatures.ssse3), LAMEXP_BOOL(cpuFeatures.x64)); - qDebug("CPU no. of cores: %d\n", cpuFeatures.count); + qDebug("CPU brand string : %s", cpuFeatures.brand); + qDebug(" CPU signature : Family: %d, Model: %d, Stepping: %d", cpuFeatures.family, cpuFeatures.model, cpuFeatures.stepping); + qDebug("CPU capabilities : MMX: %s, SSE: %s, SSE2: %s, SSE3: %s, SSSE3: %s, x64: %s", LAMEXP_BOOL(cpuFeatures.mmx), LAMEXP_BOOL(cpuFeatures.sse), LAMEXP_BOOL(cpuFeatures.sse2), LAMEXP_BOOL(cpuFeatures.sse3), LAMEXP_BOOL(cpuFeatures.ssse3), LAMEXP_BOOL(cpuFeatures.x64)); + qDebug("CPU no. of cores : %d\n", cpuFeatures.count); //Initialize Qt lamexp_init_qt(argc, argv); @@ -128,14 +128,16 @@ int lamexp_main(int argc, char* argv[]) SplashScreen::showSplash(poInitializationThread); LAMEXP_DELETE(poInitializationThread); - //Show main window + //Create main window + MainWindow *poMainWindow = new MainWindow(fileListModel, metaInfo, settingsModel); + + //Main application loop while(bAccepted) { - MainWindow *poMainWindow = new MainWindow(fileListModel, metaInfo, settingsModel); + //Show main window poMainWindow->show(); iResult = QApplication::instance()->exec(); bAccepted = poMainWindow->isAccepted(); - LAMEXP_DELETE(poMainWindow); //Show processing dialog if(bAccepted && fileListModel->rowCount() > 0) @@ -147,6 +149,7 @@ int lamexp_main(int argc, char* argv[]) } //Free models + LAMEXP_DELETE(poMainWindow); LAMEXP_DELETE(fileListModel); LAMEXP_DELETE(metaInfo); LAMEXP_DELETE(settingsModel); diff --git a/src/Model_Settings.cpp b/src/Model_Settings.cpp index 08a05e90..4b272ae0 100644 --- a/src/Model_Settings.cpp +++ b/src/Model_Settings.cpp @@ -37,6 +37,7 @@ static const char *g_settingsId_compressionRCMode = "Compression/RCMode"; static const char *g_settingsId_compressionBitrate = "Compression/Bitrate"; static const char *g_settingsId_outputDir = "OutputDirectory"; static const char *g_settingsId_writeMetaTags = "WriteMetaTags"; +static const char *g_settingsId_createPlaylist = "AutoCreatePlaylist"; #define MAKE_GETTER1(OPT,DEF) int SettingsModel::OPT(void) { return m_settings->value(g_settingsId_##OPT, DEF).toInt(); } #define MAKE_SETTER1(OPT) void SettingsModel::OPT(int value) { m_settings->setValue(g_settingsId_##OPT, value); } @@ -109,7 +110,7 @@ MAKE_SETTER1(compressionEncoder) MAKE_GETTER1(compressionRCMode, 0) MAKE_SETTER1(compressionRCMode) -MAKE_GETTER1(compressionBitrate, 0) +MAKE_GETTER1(compressionBitrate, 7) MAKE_SETTER1(compressionBitrate) MAKE_GETTER2(outputDir, QString()) @@ -117,3 +118,6 @@ MAKE_SETTER2(outputDir) MAKE_GETTER3(writeMetaTags, true) MAKE_SETTER3(writeMetaTags) + +MAKE_GETTER3(createPlaylist, true) +MAKE_SETTER3(createPlaylist) diff --git a/src/Model_Settings.h b/src/Model_Settings.h index 02549dff..c5ed184c 100644 --- a/src/Model_Settings.h +++ b/src/Model_Settings.h @@ -64,6 +64,7 @@ public: MAKE_GETTER_DEC1(compressionBitrate); MAKE_GETTER_DEC2(outputDir); MAKE_GETTER_DEC3(writeMetaTags); + MAKE_GETTER_DEC3(createPlaylist); //Setters MAKE_SETTER_DEC1(licenseAccepted); @@ -73,6 +74,7 @@ public: MAKE_SETTER_DEC1(compressionEncoder); MAKE_SETTER_DEC2(outputDir); MAKE_SETTER_DEC3(writeMetaTags); + MAKE_SETTER_DEC3(createPlaylist); void validate(void); diff --git a/src/Thread_Process.cpp b/src/Thread_Process.cpp index 980be925..38101aa9 100644 --- a/src/Thread_Process.cpp +++ b/src/Thread_Process.cpp @@ -31,10 +31,14 @@ #include #include #include +#include +#include #include #include +QMutex *ProcessThread::m_mutex_genFileName = NULL; + //////////////////////////////////////////////////////////// // Constructor //////////////////////////////////////////////////////////// @@ -47,6 +51,11 @@ ProcessThread::ProcessThread(const AudioFileModel &audioFile, const QString &out m_jobId(QUuid::createUuid()), m_aborted(false) { + if(m_mutex_genFileName) + { + m_mutex_genFileName = new QMutex; + } + connect(m_encoder, SIGNAL(statusUpdated(int)), this, SLOT(handleUpdate(int)), Qt::DirectConnection); } @@ -73,10 +82,13 @@ void ProcessThread::run() if(bSuccess) { - bSuccess = QFileInfo(outFileName).exists(); + QFileInfo fileInfo(outFileName); + bSuccess = fileInfo.exists() && fileInfo.isFile() && (fileInfo.size() > 0); } emit processStateChanged(m_jobId, (bSuccess ? "Done." : (m_aborted ? "Aborted!" : "Failed!")), (bSuccess ? ProgressModel::JobComplete : ProgressModel::JobFailed)); + emit processStateFinished(m_jobId, outFileName, bSuccess); + qDebug("Process thread is done."); } @@ -95,6 +107,8 @@ void ProcessThread::handleUpdate(int progress) QString ProcessThread::generateOutFileName(void) { + QMutexLocker lock(m_mutex_genFileName); + int n = 1; QString baseName = QFileInfo(m_audioFile.filePath()).completeBaseName(); @@ -107,6 +121,12 @@ QString ProcessThread::generateOutFileName(void) outFileName = QString("%1/%2 (%3).%4").arg(targetDir, baseName, QString::number(++n), m_encoder->extension()); } + QFile placeholder(outFileName); + if(placeholder.open(QIODevice::WriteOnly)) + { + placeholder.close(); + } + return outFileName; } @@ -114,4 +134,4 @@ QString ProcessThread::generateOutFileName(void) // EVENTS //////////////////////////////////////////////////////////// -/*NONE*/ \ No newline at end of file +/*NONE*/ diff --git a/src/Thread_Process.h b/src/Thread_Process.h index 59d01938..a46001cb 100644 --- a/src/Thread_Process.h +++ b/src/Thread_Process.h @@ -27,6 +27,8 @@ #include "Model_AudioFile.h" #include "Encoder_Abstract.h" +class QMutex; + class ProcessThread: public QThread { Q_OBJECT @@ -44,6 +46,7 @@ private slots: signals: void processStateInitialized(const QUuid &jobId, const QString &jobName, const QString &jobInitialStatus, int jobInitialState); void processStateChanged(const QUuid &jobId, const QString &newStatus, int newState); + void processStateFinished(const QUuid &jobId, const QString &outFileName, bool success); private: QString generateOutFileName(void); @@ -53,4 +56,6 @@ private: AbstractEncoder *m_encoder; const QString m_outputDirectory; volatile bool m_aborted; + + static QMutex *m_mutex_genFileName; };