OSDN Git Service

Fixed QAAC detection, hopefully.
[lamexp/LameXP.git] / src / Encoder_AAC_QAAC.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 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 #include "Global.h"
26 #include "Model_Settings.h"
27
28 #include <math.h>
29 #include <QProcess>
30 #include <QDir>
31 #include <QCoreApplication>
32
33 static int index2bitrate(const int index)
34 {
35         return (index < 32) ? ((index + 1) * 8) : ((index - 15) * 16);
36 }
37
38 static const int g_qaacVBRQualityLUT[16] = {0 ,9, 18, 27, 36, 45, 54, 63, 73, 82, 91, 100, 109, 118, 127, INT_MAX};
39
40 ///////////////////////////////////////////////////////////////////////////////
41 // Encoder Info
42 ///////////////////////////////////////////////////////////////////////////////
43
44 class QAACEncoderInfo : public AbstractEncoderInfo
45 {
46         virtual bool isModeSupported(int mode) const
47         {
48                 switch(mode)
49                 {
50                 case SettingsModel::VBRMode:
51                 case SettingsModel::CBRMode:
52                 case SettingsModel::ABRMode:
53                         return true;
54                         break;
55                 default:
56                         THROW("Bad RC mode specified!");
57                 }
58         }
59
60         virtual int valueCount(int mode) const
61         {
62                 switch(mode)
63                 {
64                 case SettingsModel::VBRMode:
65                         return 15;
66                         break;
67                 case SettingsModel::ABRMode:
68                 case SettingsModel::CBRMode:
69                         return 52;
70                         break;
71                 default:
72                         THROW("Bad RC mode specified!");
73                 }
74         }
75
76         virtual int valueAt(int mode, int index) const
77         {
78                 switch(mode)
79                 {
80                 case SettingsModel::VBRMode:
81                         return g_qaacVBRQualityLUT[qBound(0, index , 14)];
82                         break;
83                 case SettingsModel::ABRMode:
84                 case SettingsModel::CBRMode:
85                         return qBound(8, index2bitrate(index), 576);
86                         break;
87                 default:
88                         THROW("Bad RC mode specified!");
89                 }
90         }
91
92         virtual int valueType(int mode) const
93         {
94                 switch(mode)
95                 {
96                 case SettingsModel::VBRMode:
97                         return TYPE_QUALITY_LEVEL_INT;
98                         break;
99                 case SettingsModel::ABRMode:
100                         return TYPE_APPROX_BITRATE;
101                         break;
102                 case SettingsModel::CBRMode:
103                         return TYPE_BITRATE;
104                         break;
105                 default:
106                         THROW("Bad RC mode specified!");
107                 }
108         }
109
110         virtual const char *description(void) const
111         {
112                 static const char* s_description = "QAAC/QuickTime (\x0C2\x0A9 Appel)";
113                 return s_description;
114         }
115 }
116 static const g_qaacEncoderInfo;
117
118 ///////////////////////////////////////////////////////////////////////////////
119 // Encoder implementation
120 ///////////////////////////////////////////////////////////////////////////////
121
122 QAACEncoder::QAACEncoder(void)
123 :
124         m_binary_qaac(lamexp_lookup_tool("qaac.exe")),
125         m_binary_soxr(lamexp_lookup_tool("libsoxr.dll")),
126         m_binary_soxc(lamexp_lookup_tool("libsoxconvolver.dll"))
127 {
128         if(m_binary_qaac.isEmpty() || m_binary_soxr.isEmpty() || m_binary_soxc.isEmpty())
129         {
130                 THROW("Error initializing QAAC. Tool 'qaac.exe' is not registred!");
131         }
132
133         m_configProfile = 0;
134 }
135
136 QAACEncoder::~QAACEncoder(void)
137 {
138 }
139
140 bool QAACEncoder::encode(const QString &sourceFile, const AudioFileModel_MetaInfo &metaInfo, const unsigned int duration, const QString &outputFile, volatile bool *abortFlag)
141 {
142         QProcess process;
143         QStringList args;
144
145         process.setWorkingDirectory(QFileInfo(outputFile).canonicalPath());
146
147         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
148         env.insert("PATH", QDir::toNativeSeparators(QString("%1;%1/QTfiles;%2").arg(QDir(QCoreApplication::applicationDirPath()).canonicalPath(), lamexp_temp_folder2())));
149         process.setProcessEnvironment(env);
150
151         if(m_configRCMode != SettingsModel::VBRMode)
152         {
153                 switch(m_configProfile)
154                 {
155                 case 2:
156                 case 3:
157                         args << "--he"; //Forces use of HE AAC profile (there is no explicit HEv2 switch for QAAC)
158                         break;
159                 }
160         }
161
162         switch(m_configRCMode)
163         {
164         case SettingsModel::CBRMode:
165                 args << "--cbr" << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
166                 break;
167         case SettingsModel::ABRMode:
168                 args << "--abr" << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
169                 break;
170         case SettingsModel::VBRMode:
171                 args << "--tvbr" << QString::number(g_qaacVBRQualityLUT[qBound(0, m_configBitrate , 14)]);
172                 break;
173         default:
174                 THROW("Bad rate-control mode!");
175                 break;
176         }
177
178         if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
179
180         if(!metaInfo.title().isEmpty()) args << "--title" << cleanTag(metaInfo.title());
181         if(!metaInfo.artist().isEmpty()) args << "--artist" << cleanTag(metaInfo.artist());
182         if(!metaInfo.album().isEmpty()) args << "--album" << cleanTag(metaInfo.album());
183         if(!metaInfo.genre().isEmpty()) args << "--genre" << cleanTag(metaInfo.genre());
184         if(!metaInfo.comment().isEmpty()) args << "--comment" << cleanTag( metaInfo.comment());
185         if(metaInfo.year()) args << "--date" << QString::number(metaInfo.year());
186         if(metaInfo.position()) args << "--track" << QString::number(metaInfo.position());
187         if(!metaInfo.cover().isEmpty()) args << "--artwork" << metaInfo.cover();
188
189         args << "-d" << ".";
190         args << "-o" << QDir::toNativeSeparators(outputFile);
191         args << QDir::toNativeSeparators(sourceFile);
192
193         if(!startProcess(process, m_binary_qaac, args))
194         {
195                 return false;
196         }
197
198         bool bTimeout = false;
199         bool bAborted = false;
200         int prevProgress = -1;
201
202         QRegExp regExp("\\[(\\d+)\\.(\\d)%\\]");
203
204         while(process.state() != QProcess::NotRunning)
205         {
206                 if(*abortFlag)
207                 {
208                         process.kill();
209                         bAborted = true;
210                         emit messageLogged("\nABORTED BY USER !!!");
211                         break;
212                 }
213                 process.waitForReadyRead(m_processTimeoutInterval);
214                 if(!process.bytesAvailable() && process.state() == QProcess::Running)
215                 {
216                         process.kill();
217                         qWarning("QAAC process timed out <-- killing!");
218                         emit messageLogged("\nPROCESS TIMEOUT !!!");
219                         bTimeout = true;
220                         break;
221                 }
222                 while(process.bytesAvailable() > 0)
223                 {
224                         QByteArray line = process.readLine();
225                         QString text = QString::fromUtf8(line.constData()).simplified();
226                         if(regExp.lastIndexIn(text) >= 0)
227                         {
228                                 bool ok = false;
229                                 int progress = regExp.cap(1).toInt(&ok);
230                                 if(ok && (progress > prevProgress))
231                                 {
232                                         emit statusUpdated(progress);
233                                         prevProgress = qMin(progress + 2, 99);
234                                 }
235                         }
236                         else if(!text.isEmpty())
237                         {
238                                 emit messageLogged(text);
239                         }
240                 }
241         }
242
243         process.waitForFinished();
244         if(process.state() != QProcess::NotRunning)
245         {
246                 process.kill();
247                 process.waitForFinished(-1);
248         }
249         
250         emit statusUpdated(100);
251         emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
252
253         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
254         {
255                 return false;
256         }
257
258         return true;
259 }
260
261 QString QAACEncoder::extension(void)
262 {
263         return "mp4";
264 }
265
266 bool QAACEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
267 {
268         if(containerType.compare("Wave", Qt::CaseInsensitive) == 0)
269         {
270                 if(formatType.compare("PCM", Qt::CaseInsensitive) == 0)
271                 {
272                         return true;
273                 }
274         }
275
276         return false;
277 }
278
279 void QAACEncoder::setProfile(int profile)
280 {
281         m_configProfile = profile;
282 }
283
284 const AbstractEncoderInfo *QAACEncoder::getEncoderInfo(void)
285 {
286         return &g_qaacEncoderInfo;
287 }