Explicitly pass "--discard-comments" to OggEnc2 + some code refactoring.
[LameXP.git] / src / Encoder_MP3.cpp
blob73b43f95e8cab0af97c28c8336d1de5c0ecc940c
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.
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.
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.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
23 #include "Encoder_MP3.h"
25 #include "Global.h"
26 #include "Model_Settings.h"
28 #include <QProcess>
29 #include <QDir>
30 #include <limits.h>
32 static const int g_lameAgorithmQualityLUT[5] = {7, 5, 2, 0, INT_MAX};
33 static const int g_mp3BitrateLUT[15] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1};
34 static const int g_lameVBRQualityLUT[11] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, INT_MAX};
36 ///////////////////////////////////////////////////////////////////////////////
37 // Encoder Info
38 ///////////////////////////////////////////////////////////////////////////////
40 class MP3EncoderInfo : public AbstractEncoderInfo
42 virtual bool isModeSupported(int mode) const
44 switch(mode)
46 case SettingsModel::VBRMode:
47 case SettingsModel::ABRMode:
48 case SettingsModel::CBRMode:
49 return true;
50 break;
51 default:
52 MUTILS_THROW("Bad RC mode specified!");
56 virtual int valueCount(int mode) const
58 switch(mode)
60 case SettingsModel::VBRMode:
61 return 10;
62 break;
63 case SettingsModel::ABRMode:
64 case SettingsModel::CBRMode:
65 return 14;
66 break;
67 default:
68 MUTILS_THROW("Bad RC mode specified!");
72 virtual int valueAt(int mode, int index) const
74 switch(mode)
76 case SettingsModel::VBRMode:
77 return g_lameVBRQualityLUT[qBound(0, index, 9)];
78 break;
79 case SettingsModel::ABRMode:
80 case SettingsModel::CBRMode:
81 return g_mp3BitrateLUT[qBound(0, index, 13)];
82 break;
83 default:
84 MUTILS_THROW("Bad RC mode specified!");
88 virtual int valueType(int mode) const
90 switch(mode)
92 case SettingsModel::VBRMode:
93 return TYPE_QUALITY_LEVEL_INT;
94 break;
95 case SettingsModel::ABRMode:
96 return TYPE_APPROX_BITRATE;
97 break;
98 case SettingsModel::CBRMode:
99 return TYPE_BITRATE;
100 break;
101 default:
102 MUTILS_THROW("Bad RC mode specified!");
106 virtual const char *description(void) const
108 static const char* s_description = "LAME MP3 Encoder";
109 return s_description;
112 virtual const char *extension(void) const
114 static const char* s_extension = "mp3";
115 return s_extension;
118 virtual bool isResamplingSupported(void) const
120 return true;
123 static const g_mp3EncoderInfo;
125 ///////////////////////////////////////////////////////////////////////////////
126 // Encoder implementation
127 ///////////////////////////////////////////////////////////////////////////////
129 MP3Encoder::MP3Encoder(void)
131 m_binary(lamexp_tools_lookup(L1S("lame.exe")))
133 if(m_binary.isEmpty())
135 MUTILS_THROW("Error initializing MP3 encoder. Tool 'lame.exe' is not registred!");
138 m_algorithmQuality = 2;
139 m_configBitrateMaximum = 0;
140 m_configBitrateMinimum = 0;
141 m_configSamplingRate = 0;
142 m_configChannelMode = 0;
145 MP3Encoder::~MP3Encoder(void)
149 bool MP3Encoder::encode(const QString &sourceFile, const AudioFileModel_MetaInfo &metaInfo, const unsigned int duration, const QString &outputFile, volatile bool *abortFlag)
151 QProcess process;
152 QStringList args;
154 args << L1S("--nohist");
155 args << L1S("-q") << QString::number(g_lameAgorithmQualityLUT[m_algorithmQuality]);
157 switch(m_configRCMode)
159 case SettingsModel::VBRMode:
160 args << L1S("-V") << QString::number(g_lameVBRQualityLUT[qBound(0, m_configBitrate, 9)]);
161 break;
162 case SettingsModel::ABRMode:
163 args << L1S("--abr") << QString::number(g_mp3BitrateLUT[qBound(0, m_configBitrate, 13)]);
164 break;
165 case SettingsModel::CBRMode:
166 args << L1S("--cbr");
167 args << L1S("-b") << QString::number(g_mp3BitrateLUT[qBound(0, m_configBitrate, 13)]);
168 break;
169 default:
170 MUTILS_THROW("Bad rate-control mode!");
171 break;
174 if((m_configBitrateMaximum > 0) && (m_configBitrateMinimum > 0) && (m_configBitrateMinimum <= m_configBitrateMaximum))
176 if(m_configRCMode != SettingsModel::CBRMode)
178 args << L1S("-b") << QString::number(clipBitrate(m_configBitrateMinimum));
179 args << L1S("-B") << QString::number(clipBitrate(m_configBitrateMaximum));
183 if(m_configSamplingRate > 0)
185 args << L1S("--resample") << QString::number(m_configSamplingRate);
188 switch(m_configChannelMode)
190 case 1:
191 args << L1S("-m") << L1S("j");
192 break;
193 case 2:
194 args << L1S("-m") << L1S("f");
195 break;
196 case 3:
197 args << L1S("-m") << L1S("s");
198 break;
199 case 4:
200 args << L1S("-m") << L1S("d");
201 break;
202 case 5:
203 args << L1S("-m") << L1S("m");
204 break;
207 bool bUseUCS2 = false;
209 if(isUnicode(metaInfo.title())) bUseUCS2 = true;
210 if(isUnicode(metaInfo.artist())) bUseUCS2 = true;
211 if(isUnicode(metaInfo.album())) bUseUCS2 = true;
212 if(isUnicode(metaInfo.genre())) bUseUCS2 = true;
213 if(isUnicode(metaInfo.comment())) bUseUCS2 = true;
215 if (bUseUCS2) args << L1S("--id3v2-ucs2"); //Must specify this BEFORE "--tt" and friends!
217 if(!metaInfo.title().isEmpty()) args << L1S("--tt") << cleanTag(metaInfo.title());
218 if(!metaInfo.artist().isEmpty()) args << L1S("--ta") << cleanTag(metaInfo.artist());
219 if(!metaInfo.album().isEmpty()) args << L1S("--tl") << cleanTag( metaInfo.album());
220 if(!metaInfo.genre().isEmpty()) args << L1S("--tg") << cleanTag(metaInfo.genre());
221 if(!metaInfo.comment().isEmpty()) args << L1S("--tc") << cleanTag(metaInfo.comment());
222 if(metaInfo.year()) args << L1S("--ty") << QString::number(metaInfo.year());
223 if(metaInfo.position()) args << L1S("--tn") << QString::number(metaInfo.position());
224 if(!metaInfo.cover().isEmpty()) args << L1S("--ti") << QDir::toNativeSeparators(metaInfo.cover());
226 if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
228 args << QDir::toNativeSeparators(sourceFile);
229 args << QDir::toNativeSeparators(outputFile);
231 if(!startProcess(process, m_binary, args))
233 return false;
236 bool bTimeout = false;
237 bool bAborted = false;
238 int prevProgress = -1;
240 QRegExp regExp(L1S("\\(.*(\\d+)%\\)\\|"));
242 while(process.state() != QProcess::NotRunning)
244 if(*abortFlag)
246 process.kill();
247 bAborted = true;
248 emit messageLogged(L1S("\nABORTED BY USER !!!"));
249 break;
251 process.waitForReadyRead(m_processTimeoutInterval);
252 if(!process.bytesAvailable() && process.state() == QProcess::Running)
254 process.kill();
255 qWarning("LAME process timed out <-- killing!");
256 emit messageLogged(L1S("\nPROCESS TIMEOUT !!!"));
257 bTimeout = true;
258 break;
260 while(process.bytesAvailable() > 0)
262 QByteArray line = process.readLine();
263 QString text = QString::fromUtf8(line.constData()).simplified();
264 if(regExp.lastIndexIn(text) >= 0)
266 bool ok = false;
267 int progress = regExp.cap(1).toInt(&ok);
268 if(ok && (progress > prevProgress))
270 emit statusUpdated(progress);
271 prevProgress = qMin(progress + 2, 99);
274 else if(!text.isEmpty())
276 emit messageLogged(text);
281 process.waitForFinished();
282 if(process.state() != QProcess::NotRunning)
284 process.kill();
285 process.waitForFinished(-1);
288 emit statusUpdated(100);
289 emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
291 if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
293 return false;
296 return true;
299 bool MP3Encoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
301 if(containerType.compare(L1S("Wave"), Qt::CaseInsensitive) == 0)
303 if(formatType.compare(L1S("PCM"), Qt::CaseInsensitive) == 0)
305 return true;
308 else if(containerType.compare(L1S("MPEG Audio"), Qt::CaseInsensitive) == 0)
310 if(formatType.compare(L1S("MPEG Audio"), Qt::CaseInsensitive) == 0)
312 if(formatProfile.compare(L1S("Layer 3"), Qt::CaseInsensitive) == 0 || formatProfile.compare(L1S("Layer 2"), Qt::CaseInsensitive) == 0)
314 if(formatVersion.compare(L1S("Version 1"), Qt::CaseInsensitive) == 0 || formatVersion.compare(L1S("Version 2"), Qt::CaseInsensitive) == 0)
316 return true;
322 return false;
325 const unsigned int *MP3Encoder::supportedChannelCount(void)
327 static const unsigned int supportedChannels[] = {1, 2, NULL};
328 return supportedChannels;
331 void MP3Encoder::setAlgoQuality(int value)
333 m_algorithmQuality = qBound(0, value, 3);
336 void MP3Encoder::setBitrateLimits(int minimumBitrate, int maximumBitrate)
338 m_configBitrateMinimum = minimumBitrate;
339 m_configBitrateMaximum = maximumBitrate;
342 void MP3Encoder::setChannelMode(int value)
344 m_configChannelMode = value;
347 int MP3Encoder::clipBitrate(int bitrate)
349 int targetBitrate = qMin(qMax(bitrate, 32), 320);
351 int minDiff = INT_MAX;
352 int minIndx = -1;
354 for(int i = 0; g_mp3BitrateLUT[i] > 0; i++)
356 int currentDiff = abs(targetBitrate - g_mp3BitrateLUT[i]);
357 if(currentDiff < minDiff)
359 minDiff = currentDiff;
360 minIndx = i;
364 if(minIndx >= 0)
366 return g_mp3BitrateLUT[minIndx];
369 return targetBitrate;
372 const AbstractEncoderInfo *MP3Encoder::getEncoderInfo(void)
374 return &g_mp3EncoderInfo;