OSDN Git Service

Updated OggEnc2 binaries to v2.88 using libvorbis v1.3.5 and aoTuV v6.03_2015 (2015...
[lamexp/LameXP.git] / src / Encoder_Opus.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2015 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_Opus.h"
24
25 //MUtils
26 #include <MUtils/Global.h>
27
28 //Internal
29 #include "Global.h"
30 #include "Model_Settings.h"
31 #include "MimeTypes.h"
32
33 //Qt
34 #include <QProcess>
35 #include <QDir>
36 #include <QUUid>
37
38 ///////////////////////////////////////////////////////////////////////////////
39 // Encoder Info
40 ///////////////////////////////////////////////////////////////////////////////
41
42 class OpusEncoderInfo : public AbstractEncoderInfo
43 {
44         virtual bool isModeSupported(int mode) const
45         {
46                 switch(mode)
47                 {
48                 case SettingsModel::VBRMode:
49                 case SettingsModel::ABRMode:
50                 case SettingsModel::CBRMode:
51                         return true;
52                         break;
53                 default:
54                         MUTILS_THROW("Bad RC mode specified!");
55                 }
56         }
57
58         virtual int valueCount(int mode) const
59         {
60                 switch(mode)
61                 {
62                 case SettingsModel::VBRMode:
63                 case SettingsModel::ABRMode:
64                 case SettingsModel::CBRMode:
65                         return 32;
66                         break;
67                 default:
68                         MUTILS_THROW("Bad RC mode specified!");
69                 }
70         }
71
72         virtual int valueAt(int mode, int index) const
73         {
74                 switch(mode)
75                 {
76                 case SettingsModel::VBRMode:
77                 case SettingsModel::ABRMode:
78                 case SettingsModel::CBRMode:
79                         return qBound(8, (index + 1) * 8, 256);
80                         break;
81                 default:
82                         MUTILS_THROW("Bad RC mode specified!");
83                 }
84         }
85
86         virtual int valueType(int mode) const
87         {
88                 switch(mode)
89                 {
90                 case SettingsModel::VBRMode:
91                 case SettingsModel::ABRMode:
92                         return TYPE_APPROX_BITRATE;
93                         break;
94                 case SettingsModel::CBRMode:
95                         return TYPE_BITRATE;
96                         break;
97                 default:
98                         MUTILS_THROW("Bad RC mode specified!");
99                 }
100         }
101
102         virtual const char *description(void) const
103         {
104                 static const char* s_description = "Opus-Tools OpusEnc (libopus)";
105                 return s_description;
106         }
107
108         virtual const char *extension(void) const
109         {
110                 static const char* s_extension = "opus";
111                 return s_extension;
112         }
113 }
114 static const g_opusEncoderInfo;
115
116 ///////////////////////////////////////////////////////////////////////////////
117 // Encoder implementation
118 ///////////////////////////////////////////////////////////////////////////////
119
120 OpusEncoder::OpusEncoder(void)
121 :
122         m_binary(lamexp_tools_lookup("opusenc.exe"))
123 {
124         if(m_binary.isEmpty())
125         {
126                 MUTILS_THROW("Error initializing Opus encoder. Tool 'opusenc.exe' is not registred!");
127         }
128
129         m_configOptimizeFor = 0;
130         m_configEncodeComplexity = 10;
131         m_configFrameSize = 3;
132 }
133
134 OpusEncoder::~OpusEncoder(void)
135 {
136 }
137
138 bool OpusEncoder::encode(const QString &sourceFile, const AudioFileModel_MetaInfo &metaInfo, const unsigned int duration, const QString &outputFile, volatile bool *abortFlag)
139 {
140         QProcess process;
141         QStringList args;
142
143         switch(m_configRCMode)
144         {
145         case SettingsModel::VBRMode:
146                 args << "--vbr";
147                 break;
148         case SettingsModel::ABRMode:
149                 args << "--cvbr";
150                 break;
151         case SettingsModel::CBRMode:
152                 args << "--hard-cbr";
153                 break;
154         default:
155                 MUTILS_THROW("Bad rate-control mode!");
156                 break;
157         }
158
159         args << "--comp" << QString::number(m_configEncodeComplexity);
160
161         switch(m_configFrameSize)
162         {
163         case 0:
164                 args << "--framesize" << "2.5";
165                 break;
166         case 1:
167                 args << "--framesize" << "5";
168                 break;
169         case 2:
170                 args << "--framesize" << "10";
171                 break;
172         case 3:
173                 args << "--framesize" << "20";
174                 break;
175         case 4:
176                 args << "--framesize" << "40";
177                 break;
178         case 5:
179                 args << "--framesize" << "60";
180                 break;
181         }
182
183         args << QString("--bitrate") << QString::number(qBound(8, (m_configBitrate + 1) * 8, 256));
184
185         if(!metaInfo.title().isEmpty())   args << "--title"   << cleanTag(metaInfo.title());
186         if(!metaInfo.artist().isEmpty())  args << "--artist"  << cleanTag(metaInfo.artist());
187         if(!metaInfo.album().isEmpty())   args << "--album"   << cleanTag(metaInfo.album());
188         if(!metaInfo.genre().isEmpty())   args << "--genre"   << cleanTag(metaInfo.genre());
189         if(metaInfo.year())               args << "--date"    << QString::number(metaInfo.year());
190         if(metaInfo.position())           args << "--comment" << QString("tracknumber=%1").arg(QString::number(metaInfo.position()));
191         if(!metaInfo.comment().isEmpty()) args << "--comment" << QString("comment=%1").arg(cleanTag(metaInfo.comment()));
192         if(!metaInfo.cover().isEmpty())   args << "--picture" << makeCoverParam(metaInfo.cover());
193
194         if(!m_configCustomParams.isEmpty()) args << m_configCustomParams.split(" ", QString::SkipEmptyParts);
195
196         args << QDir::toNativeSeparators(sourceFile);
197         args << QDir::toNativeSeparators(outputFile);
198
199         if(!startProcess(process, m_binary, args))
200         {
201                 return false;
202         }
203
204         bool bTimeout = false;
205         bool bAborted = false;
206         int prevProgress = -1;
207
208         QRegExp regExp("\\((\\d+)%\\)");
209
210         while(process.state() != QProcess::NotRunning)
211         {
212                 if(*abortFlag)
213                 {
214                         process.kill();
215                         bAborted = true;
216                         emit messageLogged("\nABORTED BY USER !!!");
217                         break;
218                 }
219                 process.waitForReadyRead(m_processTimeoutInterval);
220                 if(!process.bytesAvailable() && process.state() == QProcess::Running)
221                 {
222                         process.kill();
223                         qWarning("Opus process timed out <-- killing!");
224                         emit messageLogged("\nPROCESS TIMEOUT !!!");
225                         bTimeout = true;
226                         break;
227                 }
228                 while(process.bytesAvailable() > 0)
229                 {
230                         QByteArray line = process.readLine();
231                         QString text = QString::fromUtf8(line.constData()).simplified();
232                         if(regExp.lastIndexIn(text) >= 0)
233                         {
234                                 bool ok = false;
235                                 int progress = regExp.cap(1).toInt(&ok);
236                                 if(ok && (progress > prevProgress))
237                                 {
238                                         emit statusUpdated(progress);
239                                         prevProgress = qMin(progress + 2, 99);
240                                 }
241                         }
242                         else if(!text.isEmpty())
243                         {
244                                 emit messageLogged(text);
245                         }
246                 }
247         }
248
249         process.waitForFinished();
250         if(process.state() != QProcess::NotRunning)
251         {
252                 process.kill();
253                 process.waitForFinished(-1);
254         }
255         
256         emit statusUpdated(100);
257         emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
258
259         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
260         {
261                 return false;
262         }
263         
264         return true;
265 }
266
267 QString OpusEncoder::detectMimeType(const QString &coverFile)
268 {
269         const QString suffix = QFileInfo(coverFile).suffix();
270         for (size_t i = 0; MIME_TYPES[i].type; i++)
271         {
272                 for (size_t k = 0; MIME_TYPES[i].ext[k]; k++)
273                 {
274                         if (suffix.compare(QString::fromLatin1(MIME_TYPES[i].ext[k]), Qt::CaseInsensitive) == 0)
275                         {
276                                 return QString::fromLatin1(MIME_TYPES[i].type);
277                         }
278                 }
279         }
280
281         qWarning("Unknown MIME type for extension '%s' -> using default!", MUTILS_UTF8(coverFile));
282         return QString::fromLatin1(MIME_TYPES[0].type);
283 }
284
285 QString OpusEncoder::makeCoverParam(const QString &coverFile)
286 {
287         return QString("3|%1|||%2").arg(detectMimeType(coverFile), QDir::toNativeSeparators(coverFile));
288 }
289
290 void OpusEncoder::setOptimizeFor(int optimizeFor)
291 {
292         m_configOptimizeFor = qBound(0, optimizeFor, 2);
293 }
294
295 void OpusEncoder::setEncodeComplexity(int complexity)
296 {
297         m_configEncodeComplexity = qBound(0, complexity, 10);
298 }
299
300 void OpusEncoder::setFrameSize(int frameSize)
301 {
302         m_configFrameSize = qBound(0, frameSize, 5);
303 }
304
305 bool OpusEncoder::isFormatSupported(const QString &containerType, const QString &containerProfile, const QString &formatType, const QString &formatProfile, const QString &formatVersion)
306 {
307         if(containerType.compare("Wave", Qt::CaseInsensitive) == 0)
308         {
309                 if(formatType.compare("PCM", Qt::CaseInsensitive) == 0)
310                 {
311                         return true;
312                 }
313         }
314
315         return false;
316 }
317
318 const unsigned int *OpusEncoder::supportedChannelCount(void)
319 {
320         return NULL;
321 }
322
323 const unsigned int *OpusEncoder::supportedBitdepths(void)
324 {
325         static const unsigned int supportedBPS[] = {8, 16, 24, AudioFileModel::BITDEPTH_IEEE_FLOAT32, NULL};
326         return supportedBPS;
327 }
328
329 const bool OpusEncoder::needsTimingInfo(void)
330 {
331         return true;
332 }
333
334 const AbstractEncoderInfo *OpusEncoder::getEncoderInfo(void)
335 {
336         return &g_opusEncoderInfo;
337 }