diff --git a/LameXP.vcproj b/LameXP.vcproj index 5cc32d0e..2c57f400 100644 --- a/LameXP.vcproj +++ b/LameXP.vcproj @@ -326,6 +326,10 @@ RelativePath=".\src\Encoder_MP3.cpp" > + + @@ -702,6 +706,40 @@ /> + + + + + + + + + + + @@ -1048,6 +1086,10 @@ RelativePath=".\tmp\MOC_Encoder_MP3.cpp" > + + diff --git a/gui/LogViewDialog.ui b/gui/LogViewDialog.ui index 4a9eb201..0b65d036 100644 --- a/gui/LogViewDialog.ui +++ b/gui/LogViewDialog.ui @@ -209,7 +209,7 @@ 10 - 6 + 8 @@ -282,6 +282,9 @@ Lucida Console + + ArrowCursor + C:\DOS C:\DOS\RUN @@ -367,6 +370,8 @@ RUN\DOS\RUN + + diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index c0863ff2..2ce47738 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -1355,6 +1355,12 @@ + + + + + + @@ -1407,7 +1413,7 @@ sourceFileView - doubleClicked(QModelIndex) + activated(QModelIndex) buttonShowDetails click() @@ -1597,22 +1603,6 @@ - - metaDataView - doubleClicked(QModelIndex) - buttonEditMeta - click() - - - 336 - 237 - - - 596 - 404 - - - writeMetaDataCheckBox clicked(bool) @@ -1645,5 +1635,21 @@ + + metaDataView + activated(QModelIndex) + buttonEditMeta + click() + + + 70 + 79 + + + 65 + 79 + + + diff --git a/gui/MetaInfo.ui b/gui/MetaInfo.ui index ed517ec7..bfeb960a 100644 --- a/gui/MetaInfo.ui +++ b/gui/MetaInfo.ui @@ -316,6 +316,8 @@ + + @@ -336,7 +338,7 @@ tableView - doubleClicked(QModelIndex) + activated(QModelIndex) editButton click() diff --git a/res/Tools.qrc b/res/Tools.qrc index df16360d..14875932 100644 --- a/res/Tools.qrc +++ b/res/Tools.qrc @@ -11,8 +11,9 @@ tools/mpcdec.exe tools/mpg123.exe tools/oggdec.exe - tools/oggenc2_gen.exe - tools/oggenc2_p4.exe + tools/oggenc2_i386.exe + tools/oggenc2_sse2.exe + tools/oggenc2_x64.exe tools/selfdelete.exe tools/shorten.exe tools/speexdec.exe diff --git a/res/tools/oggenc2_i386.exe b/res/tools/oggenc2_i386.exe new file mode 100644 index 00000000..d7559875 Binary files /dev/null and b/res/tools/oggenc2_i386.exe differ diff --git a/res/tools/oggenc2_sse2.exe b/res/tools/oggenc2_sse2.exe new file mode 100644 index 00000000..d7463c32 Binary files /dev/null and b/res/tools/oggenc2_sse2.exe differ diff --git a/res/tools/oggenc2_x64.exe b/res/tools/oggenc2_x64.exe new file mode 100644 index 00000000..81c96e73 Binary files /dev/null and b/res/tools/oggenc2_x64.exe differ diff --git a/src/Config.h b/src/Config.h index dd1aa749..54194c35 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 82 +#define VER_LAMEXP_BUILD 85 #define VER_LAMEXP_SUFFIX TechPreview /* diff --git a/src/Dialog_MainWindow.cpp b/src/Dialog_MainWindow.cpp index ffec4da1..fc65da39 100644 --- a/src/Dialog_MainWindow.cpp +++ b/src/Dialog_MainWindow.cpp @@ -522,9 +522,9 @@ void MainWindow::encodeButtonClicked(void) return; } - if(m_settings->compressionEncoder() != SettingsModel::MP3Encoder) + if(m_settings->compressionEncoder() != SettingsModel::MP3Encoder && m_settings->compressionEncoder() != SettingsModel::VorbisEncoder) { - QMessageBox::warning(this, "LameXP", "Sorry, only Lame MP3 encoding is supported at the moment!"); + QMessageBox::warning(this, "LameXP", "Sorry, only Lame MP3 and Ogg Vorbis encoding is supported at the moment.
Support for more encoders in later versions!"); tabWidget->setCurrentIndex(3); return; } @@ -1163,11 +1163,45 @@ void MainWindow::sourceFilesContextMenu(const QPoint &pos) */ void MainWindow::previewContextActionTriggered(void) { + const static char *appNames[3] = {"smplayer_portable.exe", "smplayer.exe", "mplayer.exe"}; + const static wchar_t *registryKey = L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{DB9E4EAB-2717-499F-8D56-4CC8A644AB60}"; + QModelIndex index = sourceFileView->currentIndex(); - if(index.isValid()) + if(!index.isValid()) { - QDesktopServices::openUrl(QString("file:///").append(m_fileListModel->getFile(index).filePath())); + return; } + + QString mplayerPath; + HKEY registryKeyHandle; + + if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryKey, 0, KEY_READ, ®istryKeyHandle) == ERROR_SUCCESS) + { + wchar_t Buffer[4096]; + DWORD BuffSize = sizeof(wchar_t*) * 4096; + if(RegQueryValueExW(registryKeyHandle, L"InstallLocation", 0, 0, reinterpret_cast(Buffer), &BuffSize) == ERROR_SUCCESS) + { + mplayerPath = QString::fromUtf16(reinterpret_cast(Buffer)); + } + } + + if(!mplayerPath.isEmpty()) + { + QDir mplayerDir(mplayerPath); + if(mplayerDir.exists()) + { + for(int i = 0; i < 3; i++) + { + if(mplayerDir.exists(appNames[i])) + { + QProcess::startDetached(mplayerDir.absoluteFilePath(appNames[i]), QStringList() << QDir::toNativeSeparators(m_fileListModel->getFile(index).filePath())); + return; + } + } + } + } + + QDesktopServices::openUrl(QString("file:///").append(m_fileListModel->getFile(index).filePath())); } /* diff --git a/src/Dialog_Processing.cpp b/src/Dialog_Processing.cpp index 68b45f48..0cbe6278 100644 --- a/src/Dialog_Processing.cpp +++ b/src/Dialog_Processing.cpp @@ -22,13 +22,14 @@ #include "Dialog_Processing.h" #include "Global.h" +#include "Resource.h" #include "Model_FileList.h" #include "Model_Progress.h" #include "Model_Settings.h" #include "Thread_Process.h" -#include "Encoder_MP3.h" #include "Dialog_LogView.h" -#include "Resource.h" +#include "Encoder_MP3.h" +#include "Encoder_Vorbis.h" #include #include @@ -90,7 +91,7 @@ ProcessingDialog::ProcessingDialog(FileListModel *fileListModel, AudioFileModel view_log->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); connect(m_progressModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(progressModelChanged())); connect(m_progressModel, SIGNAL(modelReset()), this, SLOT(progressModelChanged())); - connect(view_log, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(logViewDoubleClicked(QModelIndex))); + connect(view_log, SIGNAL(activated(QModelIndex)), this, SLOT(logViewDoubleClicked(QModelIndex))); //Create context menu m_contextMenu = new QMenu(); @@ -266,7 +267,7 @@ void ProcessingDialog::doneEncoding(void) if(m_userAborted) { - label_progress->setText(QString("Process was aborted by the user after %1 files!").arg(QString::number(m_succeededFiles))); + label_progress->setText((m_succeededFiles > 0) ? QString("Process was aborted by the user after %1 file(s)!").arg(QString::number(m_succeededFiles)) : "Process was aborted prematurely by the user!"); QApplication::processEvents(); PlaySound(MAKEINTRESOURCE(IDR_WAVE_ABORTED), GetModuleHandle(NULL), SND_RESOURCE | SND_SYNC); } @@ -367,17 +368,27 @@ void ProcessingDialog::startNextJob(void) encoder = mp3Encoder; } break; + case SettingsModel::VorbisEncoder: + { + VorbisEncoder *vorbisEncoder = new VorbisEncoder(); + vorbisEncoder->setBitrate(m_settings->compressionBitrate()); + vorbisEncoder->setRCMode(m_settings->compressionRCMode()); + encoder = vorbisEncoder; + } + break; default: throw "Unsupported encoder!"; } ProcessThread *thread = new ProcessThread(currentFile, (m_settings->outputToSourceDir() ? QFileInfo(currentFile.filePath()).absolutePath(): m_settings->outputDir()), encoder); m_threadList.append(thread); + 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); connect(thread, SIGNAL(processMessageLogged(QUuid,QString)), m_progressModel, SLOT(appendToLog(QUuid,QString)), Qt::QueuedConnection); + m_runningThreads++; thread->start(); } diff --git a/src/Encoder_Vorbis.cpp b/src/Encoder_Vorbis.cpp new file mode 100644 index 00000000..ee8c846b --- /dev/null +++ b/src/Encoder_Vorbis.cpp @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////// +// LameXP - Audio Encoder Front-End +// Copyright (C) 2004-2010 LoRd_MuldeR +// +// 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 +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#include "Encoder_Vorbis.h" + +#include "Global.h" +#include "Model_Settings.h" + +#include +#include + +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define IS_UNICODE(STR) (qstricmp(STR.toUtf8().constData(), QString::fromLocal8Bit(STR.toLocal8Bit()).toUtf8().constData())) + +VorbisEncoder::VorbisEncoder(void) +: + m_binary_i386(lamexp_lookup_tool("oggenc2_i386.exe")), + m_binary_sse2(lamexp_lookup_tool("oggenc2_sse2.exe")), + m_binary_x64(lamexp_lookup_tool("oggenc2_x64.exe")) +{ + if(m_binary_i386.isEmpty() || m_binary_sse2.isEmpty() || m_binary_x64.isEmpty()) + { + throw "Error initializing Vorbis encoder. Tool 'oggenc2.exe' is not registred!"; + } +} + +VorbisEncoder::~VorbisEncoder(void) +{ +} + +bool VorbisEncoder::encode(const AudioFileModel &sourceFile, const QString &outputFile, volatile bool *abortFlag) +{ + QProcess process; + QStringList args; + const QString baseName = QFileInfo(outputFile).fileName(); + lamexp_cpu_t cpuFeatures = lamexp_detect_cpu_features(); + const QString &binary = (cpuFeatures.x64 ? m_binary_x64 : ((cpuFeatures.intel && cpuFeatures.sse && cpuFeatures.sse2) ? m_binary_sse2 : m_binary_i386)); + + switch(m_configRCMode) + { + case SettingsModel::VBRMode: + args << "-q" << QString::number(max(-2, min(10, m_configBitrate))); + break; + case SettingsModel::ABRMode: + args << "-b" << QString::number(max(32, min(500, (m_configBitrate * 8)))); + break; + default: + throw "Bad rate-control mode!"; + break; + } + + if(!sourceFile.fileName().isEmpty()) args << "-t" << sourceFile.fileName(); + if(!sourceFile.fileArtist().isEmpty()) args << "-a" << sourceFile.fileArtist(); + if(!sourceFile.fileAlbum().isEmpty()) args << "-l" << sourceFile.fileAlbum(); + if(!sourceFile.fileGenre().isEmpty()) args << "-G" << sourceFile.fileGenre(); + if(!sourceFile.fileComment().isEmpty()) args << "-c" << QString("comment=%1").arg(sourceFile.fileComment()); + if(sourceFile.fileYear()) args << "-d" << QString::number(sourceFile.fileYear()); + if(sourceFile.filePosition()) args << "-N" << QString::number(sourceFile.filePosition()); + + //args << "--tv" << QString().sprintf("Encoder=LameXP v%d.%02d.%04d [%s]", lamexp_version_major(), lamexp_version_minor(), lamexp_version_build(), lamexp_version_release()); + + args << "-o" << QDir::toNativeSeparators(outputFile); + args << QDir::toNativeSeparators(sourceFile.filePath()); + + if(!startProcess(process, binary, args)) + { + return false; + } + + bool bTimeout = false; + bool bAborted = false; + + QRegExp regExp("\\[.*(\\d+)[.,](\\d+)%\\]"); + + while(process.state() != QProcess::NotRunning) + { + if(*abortFlag) + { + process.kill(); + bAborted = true; + emit messageLogged("\nABORTED BY USER !!!"); + break; + } + process.waitForReadyRead(); + if(!process.bytesAvailable() && process.state() == QProcess::Running) + { + process.kill(); + qWarning("OggEnc process timed out <-- killing!"); + bTimeout = true; + break; + } + while(process.bytesAvailable() > 0) + { + QByteArray line = process.readLine(); + QString text = QString::fromUtf8(line.constData()).simplified(); + if(regExp.lastIndexIn(text) >= 0) + { + bool ok = false; + int progress = regExp.cap(1).toInt(&ok); + if(ok) emit statusUpdated(progress); + } + else if(!text.isEmpty()) + { + emit messageLogged(text); + } + } + } + + process.waitForFinished(); + if(process.state() != QProcess::NotRunning) + { + process.kill(); + process.waitForFinished(-1); + } + + emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode())); + + if(bTimeout || bAborted || process.exitStatus() != QProcess::NormalExit) + { + return false; + } + + return true; +} + +QString VorbisEncoder::extension(void) +{ + return "ogg"; +} + +bool VorbisEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion) +{ + if(containerType.compare("Wave", Qt::CaseInsensitive) == 0) + { + if(formatType.compare("PCM", Qt::CaseInsensitive) == 0) + { + return true; + } + } + + return false; +} diff --git a/src/Encoder_Vorbis.h b/src/Encoder_Vorbis.h new file mode 100644 index 00000000..00bac598 --- /dev/null +++ b/src/Encoder_Vorbis.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////// +// LameXP - Audio Encoder Front-End +// Copyright (C) 2004-2010 LoRd_MuldeR +// +// 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 +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "Encoder_Abstract.h" + +#include + +class VorbisEncoder : public AbstractEncoder +{ + Q_OBJECT + +public: + VorbisEncoder(void); + ~VorbisEncoder(void); + + virtual bool encode(const AudioFileModel &sourceFile, const QString &outputFile, volatile bool *abortFlag); + virtual bool isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion); + virtual QString extension(void); + +private: + const QString m_binary_i386; + const QString m_binary_sse2; + const QString m_binary_x64; +}; diff --git a/src/Global.cpp b/src/Global.cpp index 50e4b818..37fc2c0a 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -294,13 +294,21 @@ lamexp_cpu_t lamexp_detect_cpu_features(void) lamexp_cpu_t features; SYSTEM_INFO systemInfo; int CPUInfo[4] = {-1}; + char CPUIdentificationString[0x40]; char CPUBrandString[0x40]; memset(&features, 0, sizeof(lamexp_cpu_t)); memset(&systemInfo, 0, sizeof(SYSTEM_INFO)); + memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString)); memset(CPUBrandString, 0, sizeof(CPUBrandString)); __cpuid(CPUInfo, 0); + memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int)); + memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int)); + memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int)); + features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0); + strcpy_s(features.vendor, 0x40, CPUIdentificationString); + if(CPUInfo[0] >= 1) { __cpuid(CPUInfo, 1); diff --git a/src/Global.h b/src/Global.h index bff555db..fe683ce0 100644 --- a/src/Global.h +++ b/src/Global.h @@ -50,8 +50,11 @@ typedef struct bool sse2; bool sse3; bool ssse3; + char vendor[0x40]; char brand[0x40]; -} lamexp_cpu_t; + bool intel; +} +lamexp_cpu_t; //LameXP version info unsigned int lamexp_version_major(void); diff --git a/src/Main.cpp b/src/Main.cpp index 7c933f0a..a3c0b336 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -66,6 +66,7 @@ int lamexp_main(int argc, char* argv[]) //Detect CPU capabilities lamexp_cpu_t cpuFeatures = lamexp_detect_cpu_features(); + qDebug(" CPU vendor id : %s (Intel: %d)", cpuFeatures.vendor, (cpuFeatures.intel ? 1 : 0)); 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)); diff --git a/src/Thread_Initialization.cpp b/src/Thread_Initialization.cpp index 23967f6e..edfd306c 100644 --- a/src/Thread_Initialization.cpp +++ b/src/Thread_Initialization.cpp @@ -50,8 +50,9 @@ static const struct lamexp_tool_t g_lamexp_tools[] = {"55c293a80475f7aeccf449ac9487a4626e5139cb", "mpcdec.exe"}, {"8bbf4a3fffe2ff143eb5ba2cf82ca16d676e865d", "mpg123.exe"}, {"437a1b193727c3dbdd557b9a58659d1ce7fbec51", "oggdec.exe"}, - {"0fb39d4e0b40ea704d90cf8740a52ceee723e60b", "oggenc2_gen.exe"}, - {"1747ecf0c8b26a56aa319900252c157467714ac1", "oggenc2_p4.exe"}, + {"ecd15abe103184aca96e406f5f1c82c6fb2e665d", "oggenc2_i386.exe"}, + {"ffe0fbd73352396dc3752ac9d484dbfc754a226d", "oggenc2_sse2.exe"}, + {"a8c50872e544a55495a824426e9378984f2ae01d", "oggenc2_x64.exe"}, {"cd95369051f96b9ca3a997658771c5ea52bc874d", "selfdelete.exe"}, {"ffeaa70bd6321185eafcb067ab2dc441650038bf", "shorten.exe"}, {"346ce516281c97e92e1b8957ddeca52edcf2d056", "speexdec.exe"}, diff --git a/src/Thread_Process.cpp b/src/Thread_Process.cpp index ba02d74a..958fdce1 100644 --- a/src/Thread_Process.cpp +++ b/src/Thread_Process.cpp @@ -66,6 +66,22 @@ ProcessThread::~ProcessThread(void) } void ProcessThread::run() +{ + try + { + processFile(); + } + catch(...) + { + fflush(stdout); + fflush(stderr); + fprintf(stderr, "\nEXCEPTION ERROR !!!\n"); + FatalAppExit(0, L"Unhandeled exception error, application will exit!"); + TerminateProcess(GetCurrentProcess(), -1); + } +} + +void ProcessThread::processFile() { m_aborted = false; @@ -169,7 +185,7 @@ QString ProcessThread::generateOutFileName(void) writeTest.remove(); } - QString outFileName = QString("%1/%2.%3").arg(targetDir.canonicalPath(), baseName, "mp3"); + QString outFileName = QString("%1/%2.%3").arg(targetDir.canonicalPath(), baseName, m_encoder->extension()); while(QFileInfo(outFileName).exists()) { outFileName = QString("%1/%2 (%3).%4").arg(targetDir.canonicalPath(), baseName, QString::number(++n), m_encoder->extension()); diff --git a/src/Thread_Process.h b/src/Thread_Process.h index 1c3c361a..6945cab1 100644 --- a/src/Thread_Process.h +++ b/src/Thread_Process.h @@ -36,7 +36,9 @@ class ProcessThread: public QThread public: ProcessThread(const AudioFileModel &audioFile, const QString &outputDirectory, AbstractEncoder *encoder); ~ProcessThread(void); + void run(); + void abort() { m_aborted = true; } QUuid getId() { return m_jobId; } @@ -51,6 +53,7 @@ signals: void processMessageLogged(const QUuid &jobId, const QString &line); private: + void processFile(); QString generateOutFileName(void); const QUuid m_jobId;