diff --git a/LameXP_VS2013.vcxproj b/LameXP_VS2013.vcxproj index 8da703a3..3735619b 100644 --- a/LameXP_VS2013.vcxproj +++ b/LameXP_VS2013.vcxproj @@ -439,6 +439,7 @@ copy /Y "$(SolutionDir)\..\Prerequisites\Qt4\$(PlatformToolset)\Shared\plugins\i $(SolutionDir)tmp\$(ProjectName)\MOC_%(Filename).cpp;%(Outputs) $(SolutionDir)tmp\$(ProjectName)\MOC_%(Filename).cpp;%(Outputs) + diff --git a/LameXP_VS2013.vcxproj.filters b/LameXP_VS2013.vcxproj.filters index 356678c4..c175cdb1 100644 --- a/LameXP_VS2013.vcxproj.filters +++ b/LameXP_VS2013.vcxproj.filters @@ -552,6 +552,9 @@ Header Files\Misc + + Header Files\Misc + diff --git a/LameXP_VS2015.vcxproj b/LameXP_VS2015.vcxproj index 09a0fb7b..cbe358fd 100644 --- a/LameXP_VS2015.vcxproj +++ b/LameXP_VS2015.vcxproj @@ -439,6 +439,7 @@ copy /Y "$(SolutionDir)\..\Prerequisites\Qt4\$(PlatformToolset)\Shared\plugins\i $(SolutionDir)tmp\$(ProjectName)\MOC_%(Filename).cpp;%(Outputs) $(SolutionDir)tmp\$(ProjectName)\MOC_%(Filename).cpp;%(Outputs) + diff --git a/LameXP_VS2015.vcxproj.filters b/LameXP_VS2015.vcxproj.filters index 356678c4..c175cdb1 100644 --- a/LameXP_VS2015.vcxproj.filters +++ b/LameXP_VS2015.vcxproj.filters @@ -552,6 +552,9 @@ Header Files\Misc + + Header Files\Misc + diff --git a/src/Config.h b/src/Config.h index 16748667..6d5d28ad 100644 --- a/src/Config.h +++ b/src/Config.h @@ -35,7 +35,7 @@ #define VER_LAMEXP_MINOR_LO 2 #define VER_LAMEXP_TYPE Beta #define VER_LAMEXP_PATCH 5 -#define VER_LAMEXP_BUILD 1794 +#define VER_LAMEXP_BUILD 1795 #define VER_LAMEXP_CONFG 1700 /////////////////////////////////////////////////////////////////////////////// diff --git a/src/Encoder_Opus.cpp b/src/Encoder_Opus.cpp index ef9e863b..d5788cd8 100644 --- a/src/Encoder_Opus.cpp +++ b/src/Encoder_Opus.cpp @@ -22,9 +22,15 @@ #include "Encoder_Opus.h" +//MUtils +#include + +//Internal #include "Global.h" #include "Model_Settings.h" +#include "MimeTypes.h" +//Qt #include #include #include @@ -183,9 +189,8 @@ bool OpusEncoder::encode(const QString &sourceFile, const AudioFileModel_MetaInf if(metaInfo.year()) args << "--date" << QString::number(metaInfo.year()); if(metaInfo.position()) args << "--comment" << QString("tracknumber=%1").arg(QString::number(metaInfo.position())); if(!metaInfo.comment().isEmpty()) args << "--comment" << QString("comment=%1").arg(cleanTag(metaInfo.comment())); - if(!metaInfo.cover().isEmpty()) args << "--picture" << QDir::toNativeSeparators(metaInfo.cover()); + if(!metaInfo.cover().isEmpty()) args << "--picture" << makeCoverParam(metaInfo.cover()); - if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts); args << QDir::toNativeSeparators(sourceFile); @@ -259,6 +264,29 @@ bool OpusEncoder::encode(const QString &sourceFile, const AudioFileModel_MetaInf return true; } +QString OpusEncoder::detectMimeType(const QString &coverFile) +{ + const QString suffix = QFileInfo(coverFile).suffix(); + for (size_t i = 0; MIME_TYPES[i].type; i++) + { + for (size_t k = 0; MIME_TYPES[i].ext[k]; k++) + { + if (suffix.compare(QString::fromLatin1(MIME_TYPES[i].ext[k]), Qt::CaseInsensitive) == 0) + { + return QString::fromLatin1(MIME_TYPES[i].type); + } + } + } + + qWarning("Unknown MIME type for extension '%s' -> using default!", MUTILS_UTF8(coverFile)); + return QString::fromLatin1(MIME_TYPES[0].type); +} + +QString OpusEncoder::makeCoverParam(const QString &coverFile) +{ + return QString("3|%1|||%2").arg(detectMimeType(coverFile), QDir::toNativeSeparators(coverFile)); +} + void OpusEncoder::setOptimizeFor(int optimizeFor) { m_configOptimizeFor = qBound(0, optimizeFor, 2); diff --git a/src/Encoder_Opus.h b/src/Encoder_Opus.h index f2ee35a5..4c21c8e6 100644 --- a/src/Encoder_Opus.h +++ b/src/Encoder_Opus.h @@ -55,4 +55,7 @@ private: int m_configOptimizeFor; int m_configEncodeComplexity; int m_configFrameSize; + + static inline QString detectMimeType(const QString &coverFile); + static inline QString makeCoverParam(const QString &coverFile); }; diff --git a/src/MimeTypes.h b/src/MimeTypes.h new file mode 100644 index 00000000..700a56af --- /dev/null +++ b/src/MimeTypes.h @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////// +// LameXP - Audio Encoder Front-End +// Copyright (C) 2004-2015 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, but always including the *additional* +// restrictions defined in the "License.txt" file. +// +// 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 + +static const struct +{ + const char *type; + const char *ext[8]; +} +MIME_TYPES[] = +{ + { "image/jpeg", { "jpg", "jpeg", "jpe", NULL } }, + { "image/png", { "png", NULL } }, + { "image/gif", { "gif", NULL } }, + { "image/tiff", { "tif", "tiff", NULL } }, + { NULL, { NULL } } +}; + +static const size_t MIME_TYPES_MAX = (sizeof(MIME_TYPES) / sizeof(MIME_TYPES[0])) - 2; diff --git a/src/Thread_FileAnalyzer_Task.cpp b/src/Thread_FileAnalyzer_Task.cpp index cc1f7b5a..003bff5b 100644 --- a/src/Thread_FileAnalyzer_Task.cpp +++ b/src/Thread_FileAnalyzer_Task.cpp @@ -26,6 +26,7 @@ #include "Global.h" #include "LockedFile.h" #include "Model_AudioFile.h" +#include "MimeTypes.h" //MUtils #include @@ -178,8 +179,8 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type readTest.close(); bool skipNext = false; - unsigned int id_val[2] = {UINT_MAX, UINT_MAX}; - cover_t coverType = coverNone; + QPair id_val(UINT_MAX, UINT_MAX); + quint32 coverType = UINT_MAX; QByteArray coverData; QStringList params; @@ -236,7 +237,7 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type QString val = line.mid(index+1).trimmed(); if(!key.isEmpty()) { - updateInfo(audioFile, &skipNext, id_val, &coverType, &coverData, key, val); + updateInfo(audioFile, skipNext, id_val, coverType, coverData, key, val); } } } @@ -271,7 +272,7 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type process.waitForFinished(-1); } - if((coverType != coverNone) && (!coverData.isEmpty())) + if((coverType != UINT_MAX) && (!coverData.isEmpty())) { retrieveCover(audioFile, coverType, coverData); } @@ -284,7 +285,7 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type return audioFile; } -void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned int *id_val, cover_t *coverType, QByteArray *coverData, const QString &key, const QString &value) +void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool &skipNext, QPair &id_val, quint32 &coverType, QByteArray &coverData, const QString &key, const QString &value) { //qWarning("'%s' -> '%s'", MUTILS_UTF8(key), MUTILS_UTF8(value)); @@ -293,7 +294,7 @@ void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned { if(value.isEmpty()) { - *skipNext = false; + skipNext = false; } else { @@ -302,15 +303,15 @@ void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned unsigned int id = value.toUInt(&ok); if(ok) { - if(IS_KEY("Gen_ID")) { id_val[0] = qMin(id_val[0], id); *skipNext = (id > id_val[0]); } - if(IS_KEY("Aud_ID")) { id_val[1] = qMin(id_val[1], id); *skipNext = (id > id_val[1]); } + if(IS_KEY("Gen_ID")) { id_val.first = qMin(id_val.first, id); skipNext = (id > id_val.first); } + if(IS_KEY("Aud_ID")) { id_val.second = qMin(id_val.second, id); skipNext = (id > id_val.second); } } else { - *skipNext = true; + skipNext = true; } } - if(*skipNext) + if(skipNext) { qWarning("Skipping info for non-primary stream!"); } @@ -318,7 +319,7 @@ void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned } /*Skip or empty?*/ - if((*skipNext) || value.isEmpty()) + if((skipNext) || value.isEmpty()) { return; } @@ -326,7 +327,7 @@ void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned /*Playlist file?*/ if(IS_KEY("Aud_Source")) { - *skipNext = true; + skipNext = true; audioFile.techInfo().setContainerType(QString()); audioFile.techInfo().setAudioType(QString()); qWarning("Skipping info for playlist file!"); @@ -382,22 +383,27 @@ void AnalyzeTask::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned } else if(IS_KEY("Gen_Cover") || IS_KEY("Gen_Cover_Type")) { - if(*coverType == coverNone) + if(coverType == UINT_MAX) { - *coverType = coverJpeg; + coverType = 0; } } else if(IS_KEY("Gen_Cover_Mime")) { QString temp = FIRST_TOK(value); - if(!temp.compare("image/jpeg", Qt::CaseInsensitive)) *coverType = coverJpeg; - else if(!temp.compare("image/png", Qt::CaseInsensitive)) *coverType = coverPng; - else if(!temp.compare("image/gif", Qt::CaseInsensitive)) *coverType = coverGif; + for (quint32 i = 0; MIME_TYPES[i].type; i++) + { + if (temp.compare(QString::fromLatin1(MIME_TYPES[i].type), Qt::CaseInsensitive) == 0) + { + coverType = i; + break; + } + } } else if(IS_KEY("Gen_Cover_Data")) { - if(!coverData->isEmpty()) coverData->clear(); - coverData->append(QByteArray::fromBase64(FIRST_TOK(value).toLatin1())); + if(!coverData.isEmpty()) coverData.clear(); + coverData.append(QByteArray::fromBase64(FIRST_TOK(value).toLatin1())); } else { @@ -483,27 +489,14 @@ bool AnalyzeTask::checkFile_CDDA(QFile &file) return ((i >= 0) && (j >= 0) && (k >= 0) && (k > j) && (j > i)); } -void AnalyzeTask::retrieveCover(AudioFileModel &audioFile, cover_t coverType, const QByteArray &coverData) +void AnalyzeTask::retrieveCover(AudioFileModel &audioFile, const quint32 coverType, const QByteArray &coverData) { - qDebug("Retrieving cover!"); - QString extension; - - switch(coverType) - { - case coverPng: - extension = QString::fromLatin1("png"); - break; - case coverGif: - extension = QString::fromLatin1("gif"); - break; - default: - extension = QString::fromLatin1("jpg"); - break; - } + qDebug("Retrieving cover! (MIME_TYPES_MAX=%u)", MIME_TYPES_MAX); - if(!(QImage::fromData(coverData, extension.toUpper().toLatin1().constData()).isNull())) + static const QString ext = QString::fromLatin1(MIME_TYPES[qBound(0U, coverType, MIME_TYPES_MAX)].ext[0]); + if(!(QImage::fromData(coverData, ext.toUpper().toLatin1().constData()).isNull())) { - QFile coverFile(QString("%1/%2.%3").arg(MUtils::temp_folder(), MUtils::rand_str(), extension)); + QFile coverFile(QString("%1/%2.%3").arg(MUtils::temp_folder(), MUtils::rand_str(), ext)); if(coverFile.open(QIODevice::WriteOnly)) { coverFile.write(coverData); diff --git a/src/Thread_FileAnalyzer_Task.h b/src/Thread_FileAnalyzer_Task.h index 3cc86784..cf600090 100644 --- a/src/Thread_FileAnalyzer_Task.h +++ b/src/Thread_FileAnalyzer_Task.h @@ -28,6 +28,7 @@ #include #include #include +#include class AudioFileModel; class QFile; @@ -49,16 +50,13 @@ public: enum fileType_t { - fileTypeNormal = 0, - fileTypeCDDA = 1, - fileTypeDenied = 2, + fileTypeNormal = 0, + fileTypeCDDA = 1, + fileTypeDenied = 2, fileTypeCueSheet = 3, - fileTypeUnknown = 4 + fileTypeUnknown = 4 }; - //Wait until there is a free slot in the queue - static bool waitForFreeSlot(volatile bool *abortFlag); - signals: void fileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file); void taskCompleted(const unsigned int taskId); @@ -68,20 +66,12 @@ protected: void run_ex(void); private: - enum cover_t - { - coverNone, - coverJpeg, - coverPng, - coverGif - }; - 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, QPair &id_val, quint32 &coverType, QByteArray &coverData, const QString &key, const QString &value); unsigned int parseYear(const QString &str); unsigned int parseDuration(const QString &str); bool checkFile_CDDA(QFile &file); - void retrieveCover(AudioFileModel &audioFile, cover_t coverType, const QByteArray &coverData); + void retrieveCover(AudioFileModel &audioFile, const quint32 coverType, const QByteArray &coverData); bool analyzeAvisynthFile(const QString &filePath, AudioFileModel &info); const unsigned int m_taskId;