OSDN Git Service

Updated VS2012 project file.
[lamexp/LameXP.git] / src / Encoder_AAC.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 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.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "Encoder_AAC.h"
23
24 #include "Global.h"
25 #include "Model_Settings.h"
26
27 #include <QProcess>
28 #include <QDir>
29
30 AACEncoder::AACEncoder(void)
31 :
32         m_binary_enc(lamexp_lookup_tool("neroAacEnc.exe")),
33         m_binary_tag(lamexp_lookup_tool("neroAacTag.exe")),
34         m_binary_sox(lamexp_lookup_tool("sox.exe"))
35 {
36         if(m_binary_enc.isEmpty() || m_binary_tag.isEmpty() || m_binary_sox.isEmpty())
37         {
38                 throw "Error initializing AAC encoder. Tool 'neroAacEnc.exe' is not registred!";
39         }
40
41         m_configProfile = 0;
42         m_configEnable2Pass = true;
43 }
44
45 AACEncoder::~AACEncoder(void)
46 {
47 }
48
49 bool AACEncoder::encode(const QString &sourceFile, const AudioFileModel &metaInfo, const QString &outputFile, volatile bool *abortFlag)
50 {
51         const unsigned int fileDuration = metaInfo.fileDuration();
52         
53         QProcess process;
54         QStringList args;
55         const QString baseName = QFileInfo(outputFile).fileName();
56
57         switch(m_configRCMode)
58         {
59         case SettingsModel::VBRMode:
60                 args << "-q" << QString().sprintf("%.2f", qBound(0.0, static_cast<double>(m_configBitrate * 5) / 100.0, 1.0));
61                 break;
62         case SettingsModel::ABRMode:
63                 args << "-br" << QString::number(qMax(32, qMin(500, (m_configBitrate * 8))) * 1000);
64                 break;
65         case SettingsModel::CBRMode:
66                 args << "-cbr" << QString::number(qMax(32, qMin(500, (m_configBitrate * 8))) * 1000);
67                 break;
68         default:
69                 throw "Bad rate-control mode!";
70                 break;
71         }
72
73         if(m_configEnable2Pass && (m_configRCMode == SettingsModel::ABRMode))
74         {
75                 args << "-2pass";
76         }
77         
78         switch(m_configProfile)
79         {
80         case 1:
81                 args << "-lc"; //Forces use of LC AAC profile
82                 break;
83         case 2:
84                 args << "-he"; //Forces use of HE AAC profile
85                 break;
86         case 3:
87                 args << "-hev2"; //Forces use of HEv2 AAC profile
88                 break;
89         }
90
91         if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
92
93         args << "-if" << QDir::toNativeSeparators(sourceFile);
94         args << "-of" << QDir::toNativeSeparators(outputFile);
95
96         if(!startProcess(process, m_binary_enc, args))
97         {
98                 return false;
99         }
100
101         bool bTimeout = false;
102         bool bAborted = false;
103         int prevProgress = -1;
104
105
106         QRegExp regExp("Processed\\s+(\\d+)\\s+seconds");
107         QRegExp regExp_pass1("First\\s+pass:\\s+processed\\s+(\\d+)\\s+seconds");
108         QRegExp regExp_pass2("Second\\s+pass:\\s+processed\\s+(\\d+)\\s+seconds");
109
110         while(process.state() != QProcess::NotRunning)
111         {
112                 if(*abortFlag)
113                 {
114                         process.kill();
115                         bAborted = true;
116                         emit messageLogged("\nABORTED BY USER !!!");
117                         break;
118                 }
119                 process.waitForReadyRead(m_processTimeoutInterval);
120                 if(!process.bytesAvailable() && process.state() == QProcess::Running)
121                 {
122                         process.kill();
123                         qWarning("NeroAacEnc process timed out <-- killing!");
124                         emit messageLogged("\nPROCESS TIMEOUT !!!");
125                         bTimeout = true;
126                         break;
127                 }
128                 while(process.bytesAvailable() > 0)
129                 {
130                         QByteArray line = process.readLine();
131                         QString text = QString::fromUtf8(line.constData()).simplified();
132                         if(regExp_pass1.lastIndexIn(text) >= 0)
133                         {
134                                 bool ok = false;
135                                 int progress = regExp_pass1.cap(1).toInt(&ok);
136                                 if(ok && (fileDuration > 0))
137                                 {
138                                         int newProgress = qRound((static_cast<double>(progress) / static_cast<double>(fileDuration)) * 50.0);
139                                         if(newProgress > prevProgress)
140                                         {
141                                                 emit statusUpdated(newProgress);
142                                                 prevProgress = qMin(newProgress + 2, 99);
143                                         }
144                                 }
145                         }
146                         else if(regExp_pass2.lastIndexIn(text) >= 0)
147                         {
148                                 bool ok = false;
149                                 int progress = regExp_pass2.cap(1).toInt(&ok);
150                                 if(ok && (fileDuration > 0))
151                                 {
152                                         int newProgress = qRound((static_cast<double>(progress) / static_cast<double>(fileDuration)) * 50.0) + 50;
153                                         if(newProgress > prevProgress)
154                                         {
155                                                 emit statusUpdated(newProgress);
156                                                 prevProgress = qMin(newProgress + 2, 99);
157                                         }
158                                 }
159                         }
160                         else if(regExp.lastIndexIn(text) >= 0)
161                         {
162                                 bool ok = false;
163                                 int progress = regExp.cap(1).toInt(&ok);
164                                 if(ok && (fileDuration > 0))
165                                 {
166                                         int newProgress = qRound((static_cast<double>(progress) / static_cast<double>(fileDuration)) * 100.0);
167                                         if(newProgress > prevProgress)
168                                         {
169                                                 emit statusUpdated(newProgress);
170                                                 prevProgress = qMin(newProgress + 2, 99);
171                                         }
172                                 }
173                         }
174                         else if(!text.isEmpty())
175                         {
176                                 emit messageLogged(text);
177                         }
178                 }
179         }
180
181         process.waitForFinished();
182         if(process.state() != QProcess::NotRunning)
183         {
184                 process.kill();
185                 process.waitForFinished(-1);
186         }
187         
188         emit statusUpdated(100);
189         emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
190
191         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
192         {
193                 return false;
194         }
195
196         emit messageLogged("\n-------------------------------\n");
197         
198         args.clear();
199         args << QDir::toNativeSeparators(outputFile);
200
201         if(!metaInfo.fileName().isEmpty()) args << QString("-meta:title=%1").arg(metaInfo.fileName());
202         if(!metaInfo.fileArtist().isEmpty()) args << QString("-meta:artist=%1").arg(metaInfo.fileArtist());
203         if(!metaInfo.fileAlbum().isEmpty()) args << QString("-meta:album=%1").arg(metaInfo.fileAlbum());
204         if(!metaInfo.fileGenre().isEmpty()) args << QString("-meta:genre=%1").arg(metaInfo.fileGenre());
205         if(!metaInfo.fileComment().isEmpty()) args << QString("-meta:comment=%1").arg(metaInfo.fileComment());
206         if(metaInfo.fileYear()) args << QString("-meta:year=%1").arg(QString::number(metaInfo.fileYear()));
207         if(metaInfo.filePosition()) args << QString("-meta:track=%1").arg(QString::number(metaInfo.filePosition()));
208         if(!metaInfo.fileCover().isEmpty()) args << QString("-add-cover:%1:%2").arg("front", metaInfo.fileCover());
209         
210         if(!startProcess(process, m_binary_tag, args))
211         {
212                 return false;
213         }
214
215         bTimeout = false;
216
217         while(process.state() != QProcess::NotRunning)
218         {
219                 if(*abortFlag)
220                 {
221                         process.kill();
222                         bAborted = true;
223                         emit messageLogged("\nABORTED BY USER !!!");
224                         break;
225                 }
226                 process.waitForReadyRead(m_processTimeoutInterval);
227                 if(!process.bytesAvailable() && process.state() == QProcess::Running)
228                 {
229                         process.kill();
230                         qWarning("NeroAacTag process timed out <-- killing!");
231                         emit messageLogged("\nPROCESS TIMEOUT !!!");
232                         bTimeout = true;
233                         break;
234                 }
235                 while(process.bytesAvailable() > 0)
236                 {
237                         QByteArray line = process.readLine();
238                         QString text = QString::fromUtf8(line.constData()).simplified();
239                         if(!text.isEmpty())
240                         {
241                                 emit messageLogged(text);
242                         }
243                 }
244         }
245
246         process.waitForFinished();
247         if(process.state() != QProcess::NotRunning)
248         {
249                 process.kill();
250                 process.waitForFinished(-1);
251         }
252                 
253         emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
254
255         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
256         {
257                 return false;
258         }
259
260         return true;
261 }
262
263 QString AACEncoder::extension(void)
264 {
265         return "mp4";
266 }
267
268 bool AACEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
269 {
270         if(containerType.compare("Wave", Qt::CaseInsensitive) == 0)
271         {
272                 if(formatType.compare("PCM", Qt::CaseInsensitive) == 0)
273                 {
274                         return true;
275                 }
276         }
277
278         return false;
279 }
280
281 void AACEncoder::setProfile(int profile)
282 {
283         m_configProfile = profile;
284 }
285
286 void AACEncoder::setEnable2Pass(bool enabled)
287 {
288         m_configEnable2Pass = enabled;
289 }
290
291 const bool AACEncoder::needsTimingInfo(void)
292 {
293         return true;
294 }