OSDN Git Service

Added project/solution files for VS2019.
[lamexp/LameXP.git] / src / Encoder_MP3.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2019 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_MP3.h"
24
25 #include "Global.h"
26 #include "Model_Settings.h"
27
28 #include <QProcess>
29 #include <QDir>
30 #include <limits.h>
31
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};
35
36 //Static
37 QMutex MP3Encoder::m_regexMutex;
38 MUtils::Lazy<QRegExp> MP3Encoder::m_regxLayer([]
39 {
40         return new QRegExp(L1S("^Layer\\s+(1|2|3)\\b"), Qt::CaseInsensitive);
41 });
42 MUtils::Lazy<QRegExp> MP3Encoder::m_regxVersion([]
43 {
44         return new QRegExp(L1S("^(Version\\s+)?(1|2|2\\.5)\\b"), Qt::CaseInsensitive);
45 });
46
47 ///////////////////////////////////////////////////////////////////////////////
48 // Encoder Info
49 ///////////////////////////////////////////////////////////////////////////////
50
51 class MP3EncoderInfo : public AbstractEncoderInfo
52 {
53         virtual bool isModeSupported(int mode) const
54         {
55                 switch(mode)
56                 {
57                 case SettingsModel::VBRMode:
58                 case SettingsModel::ABRMode:
59                 case SettingsModel::CBRMode:
60                         return true;
61                         break;
62                 default:
63                         MUTILS_THROW("Bad RC mode specified!");
64                 }
65         }
66
67         virtual int valueCount(int mode) const
68         {
69                 switch(mode)
70                 {
71                 case SettingsModel::VBRMode:
72                         return 10;
73                         break;
74                 case SettingsModel::ABRMode:
75                 case SettingsModel::CBRMode:
76                         return 14;
77                         break;
78                 default:
79                         MUTILS_THROW("Bad RC mode specified!");
80                 }
81         }
82
83         virtual int valueAt(int mode, int index) const
84         {
85                 switch(mode)
86                 {
87                 case SettingsModel::VBRMode:
88                         return g_lameVBRQualityLUT[qBound(0, index, 9)];
89                         break;
90                 case SettingsModel::ABRMode:
91                 case SettingsModel::CBRMode:
92                         return g_mp3BitrateLUT[qBound(0, index, 13)];
93                         break;
94                 default:
95                         MUTILS_THROW("Bad RC mode specified!");
96                 }
97         }
98
99         virtual int valueType(int mode) const
100         {
101                 switch(mode)
102                 {
103                 case SettingsModel::VBRMode:
104                         return TYPE_QUALITY_LEVEL_INT;
105                         break;
106                 case SettingsModel::ABRMode:
107                         return TYPE_APPROX_BITRATE;
108                         break;
109                 case SettingsModel::CBRMode:
110                         return TYPE_BITRATE;
111                         break;
112                 default:
113                         MUTILS_THROW("Bad RC mode specified!");
114                 }
115         }
116
117         virtual const char *description(void) const
118         {
119                 static const char* s_description = "LAME MP3 Encoder";
120                 return s_description;
121         }
122
123         virtual const char *extension(void) const
124         {
125                 static const char* s_extension = "mp3";
126                 return s_extension;
127         }
128
129         virtual bool isResamplingSupported(void) const
130         {
131                 return true;
132         }
133 }
134 static const g_mp3EncoderInfo;
135
136 ///////////////////////////////////////////////////////////////////////////////
137 // Encoder implementation
138 ///////////////////////////////////////////////////////////////////////////////
139
140 MP3Encoder::MP3Encoder(void)
141 :
142         m_binary(lamexp_tools_lookup(L1S("lame.exe")))
143 {
144         if(m_binary.isEmpty())
145         {
146                 MUTILS_THROW("Error initializing MP3 encoder. Tool 'lame.exe' is not registred!");
147         }
148         
149         m_algorithmQuality = 2;
150         m_configBitrateMaximum = 0;
151         m_configBitrateMinimum = 0;
152         m_configSamplingRate = 0;
153         m_configChannelMode = 0;
154 }
155
156 MP3Encoder::~MP3Encoder(void)
157 {
158 }
159
160 bool MP3Encoder::encode(const QString &sourceFile, const AudioFileModel_MetaInfo &metaInfo, const unsigned int duration, const unsigned int channels, const QString &outputFile, QAtomicInt &abortFlag)
161 {
162         QProcess process;
163         QStringList args;
164
165         args << L1S("--nohist");
166         args << L1S("-q") << QString::number(g_lameAgorithmQualityLUT[m_algorithmQuality]);
167                 
168         switch(m_configRCMode)
169         {
170         case SettingsModel::VBRMode:
171                 args << L1S("-V") << QString::number(g_lameVBRQualityLUT[qBound(0, m_configBitrate, 9)]);
172                 break;
173         case SettingsModel::ABRMode:
174                 args << L1S("--abr") << QString::number(g_mp3BitrateLUT[qBound(0, m_configBitrate, 13)]);
175                 break;
176         case SettingsModel::CBRMode:
177                 args << L1S("--cbr");
178                 args << L1S("-b") << QString::number(g_mp3BitrateLUT[qBound(0, m_configBitrate, 13)]);
179                 break;
180         default:
181                 MUTILS_THROW("Bad rate-control mode!");
182                 break;
183         }
184
185         if((m_configBitrateMaximum > 0) && (m_configBitrateMinimum > 0) && (m_configBitrateMinimum <= m_configBitrateMaximum))
186         {
187                 if(m_configRCMode != SettingsModel::CBRMode)
188                 {
189                         args << L1S("-b") << QString::number(clipBitrate(m_configBitrateMinimum));
190                         args << L1S("-B") << QString::number(clipBitrate(m_configBitrateMaximum));
191                 }
192         }
193
194         if(m_configSamplingRate > 0)
195         {
196                 args << L1S("--resample") << QString::number(m_configSamplingRate);
197         }
198
199         switch(m_configChannelMode)
200         {
201         case 1:
202                 args << L1S("-m") << L1S("j");
203                 break;
204         case 2:
205                 args << L1S("-m") << L1S("f");
206                 break;
207         case 3:
208                 args << L1S("-m") << L1S("s");
209                 break;
210         case 4:
211                 args << L1S("-m") << L1S("d");
212                 break;
213         case 5:
214                 args << L1S("-m") << L1S("m");
215                 break;
216         }
217
218         bool bUseUCS2 = false;
219
220         if(isUnicode(metaInfo.title()))   bUseUCS2 = true;
221         if(isUnicode(metaInfo.artist()))  bUseUCS2 = true;
222         if(isUnicode(metaInfo.album()))   bUseUCS2 = true;
223         if(isUnicode(metaInfo.genre()))   bUseUCS2 = true;
224         if(isUnicode(metaInfo.comment())) bUseUCS2 = true;
225
226         if (bUseUCS2) args << L1S("--id3v2-ucs2"); //Must specify this BEFORE "--tt" and friends!
227
228         if(!metaInfo.title().isEmpty())   args << L1S("--tt") << cleanTag(metaInfo.title());
229         if(!metaInfo.artist().isEmpty())  args << L1S("--ta") << cleanTag(metaInfo.artist());
230         if(!metaInfo.album().isEmpty())   args << L1S("--tl") << cleanTag( metaInfo.album());
231         if(!metaInfo.genre().isEmpty())   args << L1S("--tg") << cleanTag(metaInfo.genre());
232         if(!metaInfo.comment().isEmpty()) args << L1S("--tc") << cleanTag(metaInfo.comment());
233         if(metaInfo.year())               args << L1S("--ty") << QString::number(metaInfo.year());
234         if(metaInfo.position())           args << L1S("--tn") << QString::number(metaInfo.position());
235         if(!metaInfo.cover().isEmpty())   args << L1S("--ti") << QDir::toNativeSeparators(metaInfo.cover());
236
237         if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
238
239         args << QDir::toNativeSeparators(sourceFile);
240         args << QDir::toNativeSeparators(outputFile);
241
242         if(!startProcess(process, m_binary, args))
243         {
244                 return false;
245         }
246
247         int prevProgress = -1;
248         QRegExp regExp(L1S("\\(.*(\\d+)%\\)\\|"));
249
250         const result_t result = awaitProcess(process, abortFlag, [this, &prevProgress, &regExp](const QString &text)
251         {
252                 if (regExp.lastIndexIn(text) >= 0)
253                 {
254                         qint32 newProgress;
255                         if (MUtils::regexp_parse_int32(regExp, newProgress))
256                         {
257                                 if (newProgress > prevProgress)
258                                 {
259                                         emit statusUpdated(newProgress);
260                                         prevProgress = NEXT_PROGRESS(newProgress);
261                                 }
262                         }
263                         return true;
264                 }
265                 return false;
266         });
267
268         return (result == RESULT_SUCCESS);
269 }
270
271 bool MP3Encoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
272 {
273         if(containerType.compare(L1S("Wave"), Qt::CaseInsensitive) == 0)
274         {
275                 if(formatType.compare(L1S("PCM"), Qt::CaseInsensitive) == 0)
276                 {
277                         return true;
278                 }
279         }
280
281         static const QLatin1String mpegAudio("MPEG Audio"), waveAudio("Wave"), pcmFormat("PCM");
282         if ((containerType.compare(mpegAudio, Qt::CaseInsensitive) == 0) || (containerType.compare(waveAudio, Qt::CaseInsensitive) == 0))
283         {
284                 if (formatType.compare(pcmFormat, Qt::CaseInsensitive) == 0)
285                 {
286                         return true;
287                 }
288                 else if (formatType.compare(mpegAudio, Qt::CaseInsensitive) == 0)
289                 {
290                         QMutexLocker lock(&m_regexMutex);
291                         if (m_regxLayer->indexIn(formatProfile) >= 0)
292                         {
293                                 return (m_regxVersion->indexIn(formatVersion) >= 0);
294                         }
295                 }
296         }
297
298         return false;
299 }
300
301 const unsigned int *MP3Encoder::supportedChannelCount(void)
302 {
303         static const unsigned int supportedChannels[] = {1, 2, NULL};
304         return supportedChannels;
305 }
306
307 void MP3Encoder::setAlgoQuality(int value)
308 {
309         m_algorithmQuality = qBound(0, value, 3);
310 }
311
312 void MP3Encoder::setBitrateLimits(int minimumBitrate, int maximumBitrate)
313 {
314         m_configBitrateMinimum = minimumBitrate;
315         m_configBitrateMaximum = maximumBitrate;
316 }
317
318 void MP3Encoder::setChannelMode(int value)
319 {
320         m_configChannelMode = value;
321 }
322
323 int MP3Encoder::clipBitrate(int bitrate)
324 {
325         int targetBitrate = qMin(qMax(bitrate, 32), 320);
326         
327         int minDiff = INT_MAX;
328         int minIndx = -1;
329
330         for(int i = 0; g_mp3BitrateLUT[i] > 0; i++)
331         {
332                 int currentDiff = abs(targetBitrate - g_mp3BitrateLUT[i]);
333                 if(currentDiff < minDiff)
334                 {
335                         minDiff = currentDiff;
336                         minIndx = i;
337                 }
338         }
339
340         if(minIndx >= 0)
341         {
342                 return g_mp3BitrateLUT[minIndx];
343         }
344
345         return targetBitrate;
346 }
347
348 const AbstractEncoderInfo *MP3Encoder::getEncoderInfo(void)
349 {
350         return &g_mp3EncoderInfo;
351 }