OSDN Git Service

The "Algorithm Quality" slider now also effects the QAAC encoder.
[lamexp/LameXP.git] / src / Encoder_AAC_QAAC.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2016 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, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Encoder_AAC_QAAC.h"
24
25 //Internal
26 #include "Global.h"
27 #include "Model_Settings.h"
28
29 //MUtils
30 #include <MUtils/Global.h>
31
32 //StdLib
33 #include <math.h>
34
35 //Qt
36 #include <QProcess>
37 #include <QDir>
38 #include <QCoreApplication>
39
40 static int index2bitrate(const int index)
41 {
42         return (index < 32) ? ((index + 1) * 8) : ((index - 15) * 16);
43 }
44
45 static const int g_qaacVBRQualityLUT[16] = {0 ,9, 18, 27, 36, 45, 54, 63, 73, 82, 91, 100, 109, 118, 127, INT_MAX};
46
47 static const int RESAMPLING_QUALITY = 127;
48
49 ///////////////////////////////////////////////////////////////////////////////
50 // Encoder Info
51 ///////////////////////////////////////////////////////////////////////////////
52
53 class QAACEncoderInfo : public AbstractEncoderInfo
54 {
55         virtual bool isModeSupported(int mode) const
56         {
57                 switch(mode)
58                 {
59                 case SettingsModel::VBRMode:
60                 case SettingsModel::CBRMode:
61                 case SettingsModel::ABRMode:
62                         return true;
63                         break;
64                 default:
65                         MUTILS_THROW("Bad RC mode specified!");
66                 }
67         }
68
69         virtual int valueCount(int mode) const
70         {
71                 switch(mode)
72                 {
73                 case SettingsModel::VBRMode:
74                         return 15;
75                         break;
76                 case SettingsModel::ABRMode:
77                 case SettingsModel::CBRMode:
78                         return 52;
79                         break;
80                 default:
81                         MUTILS_THROW("Bad RC mode specified!");
82                 }
83         }
84
85         virtual int valueAt(int mode, int index) const
86         {
87                 switch(mode)
88                 {
89                 case SettingsModel::VBRMode:
90                         return g_qaacVBRQualityLUT[qBound(0, index , 14)];
91                         break;
92                 case SettingsModel::ABRMode:
93                 case SettingsModel::CBRMode:
94                         return qBound(8, index2bitrate(index), 576);
95                         break;
96                 default:
97                         MUTILS_THROW("Bad RC mode specified!");
98                 }
99         }
100
101         virtual int valueType(int mode) const
102         {
103                 switch(mode)
104                 {
105                 case SettingsModel::VBRMode:
106                         return TYPE_QUALITY_LEVEL_INT;
107                         break;
108                 case SettingsModel::ABRMode:
109                         return TYPE_APPROX_BITRATE;
110                         break;
111                 case SettingsModel::CBRMode:
112                         return TYPE_BITRATE;
113                         break;
114                 default:
115                         MUTILS_THROW("Bad RC mode specified!");
116                 }
117         }
118
119         virtual const char *description(void) const
120         {
121                 static const char* s_description = "QAAC/QuickTime (\x0C2\x0A9 Apple Inc.)";
122                 return s_description;
123         }
124
125         virtual const char *extension(void) const
126         {
127                 static const char* s_extension = "mp4";
128                 return s_extension;
129         }
130
131         virtual bool isResamplingSupported(void) const
132         {
133                 return true;
134         }
135 }
136 static const g_qaacEncoderInfo;
137
138 ///////////////////////////////////////////////////////////////////////////////
139 // Encoder implementation
140 ///////////////////////////////////////////////////////////////////////////////
141
142 QAACEncoder::QAACEncoder(void)
143 :
144         m_binary_qaac32(lamexp_tools_lookup("qaac.exe")),
145         m_binary_qaac64(lamexp_tools_lookup("qaac64.exe"))
146 {
147         if(m_binary_qaac32.isEmpty() && m_binary_qaac64.isEmpty())
148         {
149                 MUTILS_THROW("Error initializing QAAC. Tool 'qaac.exe' is not registred!");
150         }
151
152         m_configProfile = 0;
153         m_algorithmQuality = 2;
154 }
155
156 QAACEncoder::~QAACEncoder(void)
157 {
158 }
159
160 bool QAACEncoder::encode(const QString &sourceFile, const AudioFileModel_MetaInfo &metaInfo, const unsigned int duration, const QString &outputFile, volatile bool *abortFlag)
161 {
162         const QString qaac_bin = m_binary_qaac64.isEmpty() ? m_binary_qaac32 : m_binary_qaac64;
163
164         QProcess process;
165         QStringList args;
166
167         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
168         env.insert("PATH", QDir::toNativeSeparators(QString("%1;%1/QTfiles;%2").arg(QDir(QCoreApplication::applicationDirPath()).canonicalPath(), MUtils::temp_folder())));
169         process.setProcessEnvironment(env);
170
171         if(m_configRCMode != SettingsModel::VBRMode)
172         {
173                 switch(m_configProfile)
174                 {
175                 case 2:
176                 case 3:
177                         args << "--he"; //Forces use of HE AAC profile (there is no explicit HEv2 switch for QAAC)
178                         break;
179                 }
180         }
181
182         switch(m_configRCMode)
183         {
184         case SettingsModel::CBRMode:
185                 args << "--cbr" << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
186                 break;
187         case SettingsModel::ABRMode:
188                 args << "--cvbr" << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
189                 break;
190         case SettingsModel::VBRMode:
191                 args << "--tvbr" << QString::number(g_qaacVBRQualityLUT[qBound(0, m_configBitrate , 14)]);
192                 break;
193         default:
194                 MUTILS_THROW("Bad rate-control mode!");
195                 break;
196         }
197
198         args << "--quality" << QString::number(qBound(0, m_algorithmQuality, 2));
199         if (m_configSamplingRate > 0)
200         {
201                 args << QString("--native-resampler=bats,%0").arg(QString::number(RESAMPLING_QUALITY));
202                 args << "--rate" << QString::number(m_configSamplingRate);
203         }
204
205         if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
206
207         if(!metaInfo.title().isEmpty())   args << "--title"   << cleanTag(metaInfo.title());
208         if(!metaInfo.artist().isEmpty())  args << "--artist"  << cleanTag(metaInfo.artist());
209         if(!metaInfo.album().isEmpty())   args << "--album"   << cleanTag(metaInfo.album());
210         if(!metaInfo.genre().isEmpty())   args << "--genre"   << cleanTag(metaInfo.genre());
211         if(!metaInfo.comment().isEmpty()) args << "--comment" << cleanTag( metaInfo.comment());
212         if(metaInfo.year())               args << "--date"    << QString::number(metaInfo.year());
213         if(metaInfo.position())           args << "--track"   << QString::number(metaInfo.position());
214         if(!metaInfo.cover().isEmpty())   args << "--artwork" << metaInfo.cover();
215
216         args << "-d" << ".";
217         args << "-o" << QDir::toNativeSeparators(outputFile);
218         args << QDir::toNativeSeparators(sourceFile);
219
220         if(!startProcess(process, qaac_bin, args, QFileInfo(outputFile).canonicalPath()))
221         {
222                 return false;
223         }
224
225         bool bTimeout = false;
226         bool bAborted = false;
227         int prevProgress = -1;
228
229         QRegExp regExp("\\[(\\d+)\\.(\\d)%\\]");
230
231         while(process.state() != QProcess::NotRunning)
232         {
233                 if(*abortFlag)
234                 {
235                         process.kill();
236                         bAborted = true;
237                         emit messageLogged("\nABORTED BY USER !!!");
238                         break;
239                 }
240                 process.waitForReadyRead(m_processTimeoutInterval);
241                 if(!process.bytesAvailable() && process.state() == QProcess::Running)
242                 {
243                         process.kill();
244                         qWarning("QAAC process timed out <-- killing!");
245                         emit messageLogged("\nPROCESS TIMEOUT !!!");
246                         bTimeout = true;
247                         break;
248                 }
249                 while(process.bytesAvailable() > 0)
250                 {
251                         QByteArray line = process.readLine();
252                         QString text = QString::fromUtf8(line.constData()).simplified();
253                         if(regExp.lastIndexIn(text) >= 0)
254                         {
255                                 bool ok = false;
256                                 int progress = regExp.cap(1).toInt(&ok);
257                                 if(ok && (progress > prevProgress))
258                                 {
259                                         emit statusUpdated(progress);
260                                         prevProgress = qMin(progress + 2, 99);
261                                 }
262                         }
263                         else if(!text.isEmpty())
264                         {
265                                 emit messageLogged(text);
266                         }
267                 }
268         }
269
270         process.waitForFinished();
271         if(process.state() != QProcess::NotRunning)
272         {
273                 process.kill();
274                 process.waitForFinished(-1);
275         }
276         
277         emit statusUpdated(100);
278         emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
279
280         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
281         {
282                 return false;
283         }
284
285         return true;
286 }
287
288 bool QAACEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
289 {
290         if(containerType.compare("Wave", Qt::CaseInsensitive) == 0)
291         {
292                 if(formatType.compare("PCM", Qt::CaseInsensitive) == 0)
293                 {
294                         return true;
295                 }
296         }
297
298         return false;
299 }
300
301 void QAACEncoder::setProfile(int profile)
302 {
303         m_configProfile = profile;
304 }
305
306 void QAACEncoder::setAlgoQuality(int value)
307 {
308         m_algorithmQuality = qBound(0, value, 2);
309 }
310
311 const AbstractEncoderInfo *QAACEncoder::getEncoderInfo(void)
312 {
313         return &g_qaacEncoderInfo;
314 }