OSDN Git Service

Small code simplification.
[lamexp/LameXP.git] / src / Encoder_AAC_QAAC.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2021 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; always including the non-optional
9 // LAMEXP GNU GENERAL PUBLIC LICENSE ADDENDUM. See "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(L1S("qaac.exe"))),
145         m_binary_qaac64(lamexp_tools_lookup(L1S("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 unsigned int channels, const QString &outputFile, QAtomicInt &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(L1S("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 << L1S("--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 << L1S("--cbr") << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
186                 break;
187         case SettingsModel::ABRMode:
188                 args << L1S("--cvbr") << QString::number(qBound(8, index2bitrate(m_configBitrate), 576));
189                 break;
190         case SettingsModel::VBRMode:
191                 args << L1S("--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 << L1S("--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 << L1S("--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 << L1S("--title")   << cleanTag(metaInfo.title());
208         if(!metaInfo.artist().isEmpty())  args << L1S("--artist")  << cleanTag(metaInfo.artist());
209         if(!metaInfo.album().isEmpty())   args << L1S("--album")   << cleanTag(metaInfo.album());
210         if(!metaInfo.genre().isEmpty())   args << L1S("--genre")   << cleanTag(metaInfo.genre());
211         if(!metaInfo.comment().isEmpty()) args << L1S("--comment") << cleanTag( metaInfo.comment());
212         if(metaInfo.year())               args << L1S("--date")    << QString::number(metaInfo.year());
213         if(metaInfo.position())           args << L1S("--track")   << QString::number(metaInfo.position());
214         if(!metaInfo.cover().isEmpty())   args << L1S("--artwork") << metaInfo.cover();
215
216         args << L1S("-d") << L1S(".");
217         args << L1S("-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         int prevProgress = -1;
226         QRegExp regExp(L1S("\\[(\\d+)\\.(\\d)%\\]"));
227
228         const result_t result = awaitProcess(process, abortFlag, [this, &prevProgress, &regExp](const QString &text)
229         {
230                 if (regExp.lastIndexIn(text) >= 0)
231                 {
232                         qint32 newProgress;
233                         if (MUtils::regexp_parse_int32(regExp, newProgress))
234                         {
235                                 if (newProgress > prevProgress)
236                                 {
237                                         emit statusUpdated(newProgress);
238                                         prevProgress = NEXT_PROGRESS(newProgress);
239                                 }
240                         }
241                         return true;
242                 }
243                 return false;
244         });
245
246         return (result == RESULT_SUCCESS);
247 }
248
249 bool QAACEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
250 {
251         if(containerType.compare(L1S("Wave"), Qt::CaseInsensitive) == 0)
252         {
253                 if(formatType.compare(L1S("PCM"), Qt::CaseInsensitive) == 0)
254                 {
255                         return true;
256                 }
257         }
258
259         return false;
260 }
261
262 void QAACEncoder::setProfile(int profile)
263 {
264         m_configProfile = profile;
265 }
266
267 void QAACEncoder::setAlgoQuality(int value)
268 {
269         m_algorithmQuality = qBound(0, value, 2);
270 }
271
272 const AbstractEncoderInfo *QAACEncoder::getEncoderInfo(void)
273 {
274         return &g_qaacEncoderInfo;
275 }