OSDN Git Service

Updated copyright year.
[lamexp/LameXP.git] / src / Encoder_AAC_QAAC.cpp
index d0df476..ce7334e 100644 (file)
@@ -1,11 +1,12 @@
 ///////////////////////////////////////////////////////////////////////////////
 // LameXP - Audio Encoder Front-End
-// Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2023 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
+// 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.
+// (at your option) any later version; always including the non-optional
+// LAMEXP GNU GENERAL PUBLIC LICENSE ADDENDUM. See "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
 
 #include "Encoder_AAC_QAAC.h"
 
+//Internal
 #include "Global.h"
 #include "Model_Settings.h"
 
+//MUtils
+#include <MUtils/Global.h>
+
+//StdLib
 #include <math.h>
+
+//Qt
 #include <QProcess>
 #include <QDir>
 #include <QCoreApplication>
@@ -36,6 +44,8 @@ static int index2bitrate(const int index)
 
 static const int g_qaacVBRQualityLUT[16] = {0 ,9, 18, 27, 36, 45, 54, 63, 73, 82, 91, 100, 109, 118, 127, INT_MAX};
 
+static const int RESAMPLING_QUALITY = 127;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Encoder Info
 ///////////////////////////////////////////////////////////////////////////////
@@ -52,7 +62,7 @@ class QAACEncoderInfo : public AbstractEncoderInfo
                        return true;
                        break;
                default:
-                       throw "Bad RC mode specified!";
+                       MUTILS_THROW("Bad RC mode specified!");
                }
        }
 
@@ -68,7 +78,7 @@ class QAACEncoderInfo : public AbstractEncoderInfo
                        return 52;
                        break;
                default:
-                       throw "Bad RC mode specified!";
+                       MUTILS_THROW("Bad RC mode specified!");
                }
        }
 
@@ -84,7 +94,7 @@ class QAACEncoderInfo : public AbstractEncoderInfo
                        return qBound(8, index2bitrate(index), 576);
                        break;
                default:
-                       throw "Bad RC mode specified!";
+                       MUTILS_THROW("Bad RC mode specified!");
                }
        }
 
@@ -102,15 +112,26 @@ class QAACEncoderInfo : public AbstractEncoderInfo
                        return TYPE_BITRATE;
                        break;
                default:
-                       throw "Bad RC mode specified!";
+                       MUTILS_THROW("Bad RC mode specified!");
                }
        }
 
        virtual const char *description(void) const
        {
-               static const char* s_description = "QAAC/QuickTime (\x0C2\x0A9 Appel)";
+               static const char* s_description = "QAAC/QuickTime (\x0C2\x0A9 Apple Inc.)";
                return s_description;
        }
+
+       virtual const char *extension(void) const
+       {
+               static const char* s_extension = "mp4";
+               return s_extension;
+       }
+
+       virtual bool isResamplingSupported(void) const
+       {
+               return true;
+       }
 }
 static const g_qaacEncoderInfo;
 
@@ -120,30 +141,31 @@ static const g_qaacEncoderInfo;
 
 QAACEncoder::QAACEncoder(void)
 :
-       m_binary_enc(lamexp_lookup_tool("qaac.exe")),
-       m_binary_dll(lamexp_lookup_tool("libsoxrate.dll"))
+       m_binary_qaac32(lamexp_tools_lookup(L1S("qaac.exe"))),
+       m_binary_qaac64(lamexp_tools_lookup(L1S("qaac64.exe")))
 {
-       if(m_binary_enc.isEmpty() || m_binary_dll.isEmpty())
+       if(m_binary_qaac32.isEmpty() && m_binary_qaac64.isEmpty())
        {
-               throw "Error initializing QAAC. Tool 'qaac.exe' is not registred!";
+               MUTILS_THROW("Error initializing QAAC. Tool 'qaac.exe' is not registred!");
        }
 
        m_configProfile = 0;
+       m_algorithmQuality = 2;
 }
 
 QAACEncoder::~QAACEncoder(void)
 {
 }
 
-bool QAACEncoder::encode(const QString &sourceFile, const AudioFileModel &metaInfo, const QString &outputFile, volatile bool *abortFlag)
+bool QAACEncoder::encode(const QString &sourceFile, const AudioFileModel_MetaInfo &metaInfo, const unsigned int /*duration*/, const unsigned int /*channels*/, const QString &outputFile, QAtomicInt &abortFlag)
 {
+       const QString qaac_bin = m_binary_qaac64.isEmpty() ? m_binary_qaac32 : m_binary_qaac64;
+
        QProcess process;
        QStringList args;
 
-       process.setWorkingDirectory(QFileInfo(outputFile).canonicalPath());
-
        QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
-       env.insert("PATH", QDir::toNativeSeparators(QString("%1;%1/QTfiles;%2").arg(QDir(QCoreApplication::applicationDirPath()).canonicalPath(), lamexp_temp_folder2())));
+       env.insert(L1S("PATH"), QDir::toNativeSeparators(QString("%1;%1/QTfiles;%2").arg(QDir(QCoreApplication::applicationDirPath()).canonicalPath(), MUtils::temp_folder())));
        process.setProcessEnvironment(env);
 
        if(m_configRCMode != SettingsModel::VBRMode)
@@ -152,7 +174,7 @@ bool QAACEncoder::encode(const QString &sourceFile, const AudioFileModel &metaIn
                {
                case 2:
                case 3:
-                       args << "--he"; //Forces use of HE AAC profile (there is no explicit HEv2 switch for QAAC)
+                       args << L1S("--he"); //Forces use of HE AAC profile (there is no explicit HEv2 switch for QAAC)
                        break;
                }
        }
@@ -160,112 +182,75 @@ bool QAACEncoder::encode(const QString &sourceFile, const AudioFileModel &metaIn
        switch(m_configRCMode)
        {
        case SettingsModel::CBRMode:
-               args << "--cbr" << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
+               args << L1S("--cbr") << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
                break;
        case SettingsModel::ABRMode:
-               args << "--abr" << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
+               args << L1S("--cvbr") << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
                break;
        case SettingsModel::VBRMode:
-               args << "--tvbr" << QString::number(g_qaacVBRQualityLUT[qBound(0, m_configBitrate , 14)]);
+               args << L1S("--tvbr") << QString::number(g_qaacVBRQualityLUT[qBound(0, m_configBitrate , 14)]);
                break;
        default:
-               throw "Bad rate-control mode!";
+               MUTILS_THROW("Bad rate-control mode!");
                break;
        }
 
+       args << L1S("--quality") << QString::number(qBound(0, m_algorithmQuality, 2));
+       if (m_configSamplingRate > 0)
+       {
+               args << QString("--native-resampler=bats,%0").arg(QString::number(RESAMPLING_QUALITY));
+               args << L1S("--rate") << QString::number(m_configSamplingRate);
+       }
+
        if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
 
-       if(!metaInfo.fileName().isEmpty()) args << "--title" << cleanTag(metaInfo.fileName());
-       if(!metaInfo.fileArtist().isEmpty()) args << "--artist" << cleanTag(metaInfo.fileArtist());
-       if(!metaInfo.fileAlbum().isEmpty()) args << "--album" << cleanTag(metaInfo.fileAlbum());
-       if(!metaInfo.fileGenre().isEmpty()) args << "--genre" << cleanTag(metaInfo.fileGenre());
-       if(!metaInfo.fileComment().isEmpty()) args << "--comment" << cleanTag( metaInfo.fileComment());
-       if(metaInfo.fileYear()) args << "--date" << QString::number(metaInfo.fileYear());
-       if(metaInfo.filePosition()) args << "--track" << QString::number(metaInfo.filePosition());
-       if(!metaInfo.fileCover().isEmpty()) args << "--artwork" << metaInfo.fileCover();
-
-       args << "-d" << ".";
-       args << "-o" << QDir::toNativeSeparators(outputFile);
+       if(!metaInfo.title().isEmpty())   args << L1S("--title")   << cleanTag(metaInfo.title());
+       if(!metaInfo.artist().isEmpty())  args << L1S("--artist")  << cleanTag(metaInfo.artist());
+       if(!metaInfo.album().isEmpty())   args << L1S("--album")   << cleanTag(metaInfo.album());
+       if(!metaInfo.genre().isEmpty())   args << L1S("--genre")   << cleanTag(metaInfo.genre());
+       if(!metaInfo.comment().isEmpty()) args << L1S("--comment") << cleanTag( metaInfo.comment());
+       if(metaInfo.year())               args << L1S("--date")    << QString::number(metaInfo.year());
+       if(metaInfo.position())           args << L1S("--track")   << QString::number(metaInfo.position());
+       if(!metaInfo.cover().isEmpty())   args << L1S("--artwork") << metaInfo.cover();
+
+       args << L1S("-d") << L1S(".");
+       args << L1S("-o") << QDir::toNativeSeparators(outputFile);
        args << QDir::toNativeSeparators(sourceFile);
 
-       if(!startProcess(process, m_binary_enc, args))
+       if(!startProcess(process, qaac_bin, args, QFileInfo(outputFile).canonicalPath()))
        {
                return false;
        }
 
-       bool bTimeout = false;
-       bool bAborted = false;
        int prevProgress = -1;
+       QRegExp regExp(L1S("\\[(\\d+)\\.(\\d)%\\]"));
 
-       QRegExp regExp("\\[(\\d+)\\.(\\d)%\\]");
-
-       while(process.state() != QProcess::NotRunning)
+       const result_t result = awaitProcess(process, abortFlag, [this, &prevProgress, &regExp](const QString &text)
        {
-               if(*abortFlag)
+               if (regExp.lastIndexIn(text) >= 0)
                {
-                       process.kill();
-                       bAborted = true;
-                       emit messageLogged("\nABORTED BY USER !!!");
-                       break;
-               }
-               process.waitForReadyRead(m_processTimeoutInterval);
-               if(!process.bytesAvailable() && process.state() == QProcess::Running)
-               {
-                       process.kill();
-                       qWarning("QAAC process timed out <-- killing!");
-                       emit messageLogged("\nPROCESS TIMEOUT !!!");
-                       bTimeout = true;
-                       break;
-               }
-               while(process.bytesAvailable() > 0)
-               {
-                       QByteArray line = process.readLine();
-                       QString text = QString::fromUtf8(line.constData()).simplified();
-                       if(regExp.lastIndexIn(text) >= 0)
+                       qint32 newProgress;
+                       if (MUtils::regexp_parse_int32(regExp, newProgress))
                        {
-                               bool ok = false;
-                               int progress = regExp.cap(1).toInt(&ok);
-                               if(ok && (progress > prevProgress))
+                               if (newProgress > prevProgress)
                                {
-                                       emit statusUpdated(progress);
-                                       prevProgress = qMin(progress + 2, 99);
+                                       emit statusUpdated(newProgress);
+                                       prevProgress = NEXT_PROGRESS(newProgress);
                                }
                        }
-                       else if(!text.isEmpty())
-                       {
-                               emit messageLogged(text);
-                       }
+                       return true;
                }
-       }
-
-       process.waitForFinished();
-       if(process.state() != QProcess::NotRunning)
-       {
-               process.kill();
-               process.waitForFinished(-1);
-       }
-       
-       emit statusUpdated(100);
-       emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
-
-       if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
-       {
                return false;
-       }
+       });
 
-       return true;
+       return (result == RESULT_SUCCESS);
 }
 
-QString QAACEncoder::extension(void)
+bool QAACEncoder::isFormatSupported(const QString &containerType, const QString& /*containerProfile*/, const QString &formatType, const QString& /*formatProfile*/, const QString& /*formatVersion*/)
 {
-       return "mp4";
-}
-
-bool QAACEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
-{
-       if(containerType.compare("Wave", Qt::CaseInsensitive) == 0)
+       if(containerType.compare(L1S("Wave"), Qt::CaseInsensitive) == 0)
        {
-               if(formatType.compare("PCM", Qt::CaseInsensitive) == 0)
+               if(formatType.compare(L1S("PCM"), Qt::CaseInsensitive) == 0)
                {
                        return true;
                }
@@ -279,6 +264,11 @@ void QAACEncoder::setProfile(int profile)
        m_configProfile = profile;
 }
 
+void QAACEncoder::setAlgoQuality(int value)
+{
+       m_algorithmQuality = qBound(0, value, 2);
+}
+
 const AbstractEncoderInfo *QAACEncoder::getEncoderInfo(void)
 {
        return &g_qaacEncoderInfo;