1 /*************************************************************************
2 MP3Encoder.cpp - export of MP3 data via "lame"
3 -------------------
4 begin : Sat May 19 2012
5 copyright : (C) 2012 by Thomas Eschenbacher
6 email : Thomas.Eschenbacher@gmx.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "config.h"
19
20 #include <math.h>
21 #include <new>
22
23 #include <id3/globals.h>
24 #include <id3/misc_support.h>
25 #include <id3/tag.h>
26
27 #include <QByteArray>
28 #include <QDate>
29 #include <QDateTime>
30 #include <QLatin1Char>
31 #include <QList>
32 #include <QMap>
33
34 #include <KLocalizedString>
35
36 #include "libkwave/FileInfo.h"
37 #include "libkwave/GenreType.h"
38 #include "libkwave/MessageBox.h"
39 #include "libkwave/MetaDataList.h"
40 #include "libkwave/MixerMatrix.h"
41 #include "libkwave/MultiTrackReader.h"
42 #include "libkwave/Sample.h"
43 #include "libkwave/SampleReader.h"
44 #include "libkwave/String.h"
45 #include "libkwave/Utils.h"
46
47 #include "ID3_QIODeviceWriter.h"
48 #include "MP3CodecPlugin.h"
49 #include "MP3Encoder.h"
50 #include "MP3EncoderSettings.h"
51
52 /***************************************************************************/
MP3Encoder()53 Kwave::MP3Encoder::MP3Encoder()
54 :Kwave::Encoder(),
55 m_property_map(),
56 m_lock(),
57 m_dst(Q_NULLPTR),
58 m_process(this),
59 m_program(),
60 m_params()
61 {
62 REGISTER_MIME_TYPES
63 REGISTER_COMPRESSION_TYPES
64
65 connect(&m_process, SIGNAL(readyReadStandardOutput()),
66 this, SLOT(dataAvailable()));
67 }
68
69 /***************************************************************************/
~MP3Encoder()70 Kwave::MP3Encoder::~MP3Encoder()
71 {
72 }
73
74 /***************************************************************************/
instance()75 Kwave::Encoder *Kwave::MP3Encoder::instance()
76 {
77 return new(std::nothrow) MP3Encoder();
78 }
79
80 /***************************************************************************/
supportedProperties()81 QList<Kwave::FileProperty> Kwave::MP3Encoder::supportedProperties()
82 {
83 return m_property_map.properties();
84 }
85
86 /***************************************************************************/
encodeID3Tags(const Kwave::MetaDataList & meta_data,ID3_Tag & tag)87 void Kwave::MP3Encoder::encodeID3Tags(const Kwave::MetaDataList &meta_data,
88 ID3_Tag &tag)
89 {
90 const Kwave::FileInfo info(meta_data);
91 ID3_FrameInfo frameInfo;
92
93 const QMap<Kwave::FileProperty, QVariant> properties(info.properties());
94 QMap<Kwave::FileProperty, QVariant>::const_iterator it;
95 for (it = properties.begin(); it != properties.end(); ++it) {
96 const Kwave::FileProperty &property = it.key();
97 const QVariant &value = it.value();
98
99 ID3_FrameID id = m_property_map.findProperty(property);
100 if (id == ID3FID_NOFRAME) continue;
101
102 if (info.contains(Kwave::INF_CD) && (property == Kwave::INF_CDS))
103 continue; /* INF_CDS has already been handled by INF_CD */
104 if (info.contains(Kwave::INF_TRACK) && (property == Kwave::INF_TRACKS))
105 continue; /* INF_TRACKS has already been handled by INF_TRACK */
106
107 ID3_Frame *frame = new(std::nothrow) ID3_Frame;
108 Q_ASSERT(frame);
109 if (!frame) break;
110
111 QString str_val = value.toString();
112 // qDebug("encoding ID3 tag #%02d, property='%s', value='%s'",
113 // static_cast<int>(id),
114 // DBG(info.name(property)),
115 // DBG(str_val)
116 // );
117
118 // encode in UCS16
119 frame->SetID(id);
120 ID3_Field *field = frame->GetField(ID3FN_TEXT);
121 if (!field) {
122 qWarning("no field, frame id=%d", static_cast<int>(id));
123 delete frame;
124 continue;
125 }
126
127 ID3_PropertyMap::Encoding encoding = m_property_map.encoding(id);
128 switch (encoding) {
129 case ID3_PropertyMap::ENC_TEXT_PARTINSET:
130 {
131 field->SetEncoding(ID3TE_UTF16);
132
133 // if "number of CDs is available: append with "/"
134 int cds = info.get(Kwave::INF_CDS).toInt();
135 if (cds > 0)
136 str_val += _("/%1").arg(cds);
137
138 field->Set(static_cast<const unicode_t *>(str_val.utf16()));
139 break;
140 }
141 case ID3_PropertyMap::ENC_TRACK_NUM:
142 {
143 // if "number of tracks is available: append with "/"
144 int tracks = info.get(Kwave::INF_TRACKS).toInt();
145 if (tracks > 0)
146 str_val += _("/%1").arg(tracks);
147
148 field->SetEncoding(ID3TE_UTF16);
149 field->Set(static_cast<const unicode_t *>(str_val.utf16()));
150 break;
151 }
152 case ID3_PropertyMap::ENC_TERMS_OF_USE:
153 // the same as ENC_COMMENT, but without "Description"
154 /* FALLTHROUGH */
155 case ID3_PropertyMap::ENC_COMMENT:
156 {
157 // detect language at the start "[xxx] "
158 QString lang;
159 if (str_val.startsWith(QLatin1Char('[')) &&
160 (str_val.at(4) == QLatin1Char(']'))) {
161 lang = str_val.mid(1,3);
162 str_val = str_val.mid(5);
163 frame->GetField(ID3FN_DESCRIPTION)->Set("");
164 frame->GetField(ID3FN_LANGUAGE)->Set(
165 static_cast<const char *>(lang.toLatin1().data()));
166 }
167 /* frame->GetField(ID3FN_DESCRIPTION)->Set(""); */
168 field->SetEncoding(ID3TE_UTF16);
169 field->Set(static_cast<const unicode_t *>(str_val.utf16()));
170 break;
171 }
172 case ID3_PropertyMap::ENC_GENRE_TYPE:
173 {
174 int genre = Kwave::GenreType::fromID3(str_val);
175 if (genre >= 0)
176 str_val = Kwave::GenreType::name(genre, false);
177 // else: user defined genre type, take it as it is
178
179 field->SetEncoding(ID3TE_UTF16);
180 field->Set(static_cast<const unicode_t *>(str_val.utf16()));
181 break;
182 }
183 case ID3_PropertyMap::ENC_LENGTH:
184 {
185 // length in milliseconds
186 const double rate = info.rate();
187 const sample_index_t samples = info.length();
188 if ((rate > 0) && samples) {
189 const sample_index_t ms = static_cast<sample_index_t>(
190 (static_cast<double>(samples) * 1E3) / rate);
191
192 str_val = QString::number(ms);
193
194 field->SetEncoding(ID3TE_UTF16);
195 field->Set(static_cast<const unicode_t *>(str_val.utf16()));
196 } else {
197 delete frame;
198 frame = Q_NULLPTR;
199 }
200 break;
201 }
202 case ID3_PropertyMap::ENC_TEXT_TIMESTAMP:
203 {
204 // ISO 8601 timestamp: "yyyy-MM-ddTHH:mm:ss"
205 QString s = Kwave::string2date(str_val);
206
207 // if failed, try "yyyy" format (year only)
208 if (!s.length()) {
209 int year = str_val.toInt();
210 if ((year > 0) && (year < 9999)) {
211 frame->SetID(ID3FID_YEAR);
212 // -> re-get the field !
213 // it has become invalid through "SetID()"
214 field = frame->GetField(ID3FN_TEXT);
215 if (!field) {
216 qWarning("no field, frame id=%d",
217 static_cast<int>(id));
218 break;
219 }
220 s = _("%1").arg(year, 4, 10, QLatin1Char('0'));
221 }
222 }
223
224 if (s.length()) {
225 field->SetEncoding(ID3TE_UTF16);
226 field->Set(static_cast<const unicode_t *>(s.utf16()));
227 } else {
228 // date is invalid, unknown format
229 qWarning("MP3Encoder::encodeID3Tags(): invalid date: '%s'",
230 DBG(str_val));
231 delete frame;
232 frame = Q_NULLPTR;
233 }
234 break;
235 }
236 case ID3_PropertyMap::ENC_TEXT_SLASH: /* FALLTHROUGH */
237 case ID3_PropertyMap::ENC_TEXT_URL: /* FALLTHROUGH */
238 case ID3_PropertyMap::ENC_TEXT:
239 field->SetEncoding(ID3TE_UTF16);
240 field->Set(static_cast<const unicode_t *>(str_val.utf16()));
241 break;
242 case ID3_PropertyMap::ENC_NONE: /* FALLTHROUGH */
243 default:
244 // ignore
245 delete frame;
246 frame = Q_NULLPTR;
247 break;
248 }
249
250 if (frame) tag.AttachFrame(frame);
251 }
252
253 tag.Strip();
254 tag.Update();
255 }
256
257 #define OPTION(__field__) \
258 if (settings.__field__.length()) m_params.append(settings.__field__)
259
260 #define OPTION_P(__field__, __value__) \
261 if (settings.__field__.length()) \
262 m_params.append( \
263 QString(settings.__field__.arg(__value__)).split(QLatin1Char(' ')))
264
265 /***************************************************************************/
encode(QWidget * widget,Kwave::MultiTrackReader & src,QIODevice & dst,const Kwave::MetaDataList & meta_data)266 bool Kwave::MP3Encoder::encode(QWidget *widget, Kwave::MultiTrackReader &src,
267 QIODevice &dst,
268 const Kwave::MetaDataList &meta_data)
269 {
270 bool result = true;
271 ID3_Tag id3_tag;
272 Kwave::MP3EncoderSettings settings;
273
274 settings.load();
275
276 ID3_TagType id3_tag_type = ID3TT_ID3V2;
277 id3_tag.SetSpec(ID3V2_LATEST);
278
279 const Kwave::FileInfo info(meta_data);
280
281 // get info: tracks, sample rate
282 const unsigned int tracks = src.tracks();
283 const sample_index_t length = src.last() - src.first() + 1;
284 unsigned int bits = qBound(8U, ((info.bits() + 7) & ~0x7), 32U);
285 const double rate = info.rate();
286 const unsigned int out_tracks = qMin(tracks, 2U);
287
288 // when encoding track count > 2, show a warning that we will mix down
289 // to stereo
290 if (tracks > 2) {
291 if (Kwave::MessageBox::warningContinueCancel(
292 widget,
293 i18n("The file format you have chosen supports only mono or "
294 "stereo. This file will be mixed down to stereo when "
295 "saving."),
296 QString(), QString(), QString(),
297 _("mp3_accept_down_mix_on_export")) != KMessageBox::Continue)
298 {
299 return false;
300 }
301 }
302
303 // open the output device
304 if (!dst.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
305 Kwave::MessageBox::error(widget,
306 i18n("Unable to open the file for saving!"));
307 return false;
308 }
309
310 m_dst = &dst;
311 m_params.clear();
312
313 // encode meta data into with id3lib
314 ID3_QIODeviceWriter id3_writer(dst);
315 encodeID3Tags(meta_data, id3_tag);
316
317 OPTION(m_flags.m_prepend); // optional parameters at the very start
318
319 // mandantory audio input format and encoding options
320 OPTION(m_input.m_raw_format); // input is raw audio
321 OPTION(m_input.m_byte_order); // byte swapping
322 OPTION(m_input.m_signed); // signed sample format
323
324 // supported sample rates [kHz]
325 // 8 / 11.025 / 12 / 16 / 22.05 / 24 /32 / 44.1 / 48
326 // if our rate is not supported, lame automatically resamples with the
327 // next higher supported rate
328 if (settings.m_format.m_sample_rate.length()) {
329 QString str = settings.m_format.m_sample_rate;
330 if (str.contains(_("[%khz]"))) {
331 str = str.replace(_("[%khz]"),
332 _("%1")).arg(rate / 1000.0, 1, 'f', 2);
333 m_params.append(str.split(QLatin1Char(' ')));
334 } else {
335 m_params.append(str.arg(rate).split(QLatin1Char(' ')));
336 }
337 }
338
339 // bits per sample, supported by Kwave are: 8 / 16 / 24 / 32
340 if (!settings.m_format.m_bits_per_sample.contains(QLatin1Char('%'))) {
341 // bits/sample are not selectable => use default=16bit
342 bits = 16;
343 OPTION(m_format.m_bits_per_sample);
344 } else {
345 OPTION_P(m_format.m_bits_per_sample, bits);
346 }
347
348 // encode one track as "mono" and two tracks as "joint-stereo"
349 if (tracks == 1) {
350 OPTION(m_format.m_channels.m_mono);
351 } else {
352 OPTION(m_format.m_channels.m_stereo);
353 }
354
355 // nominal / lower / upper bitrate
356 int bitrate_min = 8;
357 int bitrate_max = 320;
358 int bitrate_nom = 128;
359 if (info.contains(Kwave::INF_BITRATE_NOMINAL)) {
360 // nominal bitrate => use ABR mode
361 bitrate_nom = info.get(Kwave::INF_BITRATE_NOMINAL).toInt() / 1000;
362 bitrate_nom = qBound(bitrate_min, bitrate_nom, bitrate_max);
363 OPTION_P(m_quality.m_bitrate.m_avg, bitrate_nom);
364 }
365 if (info.contains(Kwave::INF_BITRATE_LOWER)) {
366 int bitrate = info.get(Kwave::INF_BITRATE_LOWER).toInt() / 1000;
367 bitrate_min = qBound(bitrate_min, bitrate, bitrate_nom);
368 OPTION_P(m_quality.m_bitrate.m_min, bitrate_min);
369 }
370 if (info.contains(Kwave::INF_BITRATE_UPPER)) {
371 int bitrate = info.get(Kwave::INF_BITRATE_UPPER).toInt() / 1000;
372 bitrate_max = qBound(bitrate_nom, bitrate, bitrate_max);
373 OPTION_P(m_quality.m_bitrate.m_max, bitrate_max);
374 }
375 // Kwave::INF_MPEG_LAYER, /**< MPEG Layer, I/II/III */
376 // Kwave::INF_MPEG_MODEEXT, /**< MPEG mode extension */
377 // Kwave::INF_MPEG_VERSION, /**< MPEG version */
378
379 /* MPEG emphasis mode */
380 if (info.contains(Kwave::INF_MPEG_EMPHASIS)) {
381 int emphasis = info.get(Kwave::INF_MPEG_EMPHASIS).toInt();
382 switch (emphasis) {
383 case 1:
384 OPTION(m_encoding.m_emphasis.m_50_15ms); // 1 = 50/15ms
385 break;
386 case 3:
387 OPTION(m_encoding.m_emphasis.m_ccit_j17); // 3 = CCIT J.17
388 break;
389 case 0: /* FALLTHROUGH */
390 default:
391 OPTION(m_encoding.m_emphasis.m_none); // 0 = none
392 break;
393 }
394 }
395
396 OPTION(m_encoding.m_noise_shaping); // noise shaping settings
397 OPTION(m_encoding.m_compatibility); // compatibility options
398
399 if (info.contains(Kwave::INF_COPYRIGHTED) && info.get(Kwave::INF_COPYRIGHTED).toBool()) {
400 OPTION(m_flags.m_copyright); // copyrighted
401 }
402
403 if (info.contains(Kwave::INF_ORIGINAL) && !info.get(Kwave::INF_ORIGINAL).toBool()) {
404 OPTION(m_flags.m_original); // original
405 }
406
407 OPTION(m_flags.m_protect); // CRC protection
408 OPTION(m_flags.m_append); // optional parameters at the end
409
410 m_params.append(_("-")); // infile = stdin
411 m_params.append(_("-")); // outfile = stdout
412
413 m_program = settings.m_path;
414
415 qDebug("MP3Encoder::encode(): %s %s",
416 DBG(m_program), DBG(m_params.join(_(" ")))
417 );
418
419 m_process.setReadChannel(QProcess::StandardOutput);
420
421 m_process.start(m_program, m_params);
422 QString stdError;
423 if (!m_process.waitForStarted()) {
424 qWarning("cannot start program '%s'", DBG(m_program));
425 m_process.waitForFinished();
426 result = false;
427 }
428
429 // if a ID3v2 tag is requested, the tag comes at the start
430 if (id3_tag_type == ID3TT_ID3V2)
431 id3_tag.Render(id3_writer, id3_tag_type);
432
433 // MP3 supports only mono and stereo, prepare a mixer matrix
434 // (not used in case of tracks <= 2)
435 Kwave::MixerMatrix mixer(tracks, out_tracks);
436
437 // read in from the sample readers
438 const unsigned int buf_len = sizeof(m_write_buffer);
439 const int bytes_per_sample = bits / 8;
440
441 sample_index_t rest = length;
442 Kwave::SampleArray in_samples(tracks);
443 Kwave::SampleArray out_samples(tracks);
444
445 while (result && rest && (m_process.state() != QProcess::NotRunning)) {
446 unsigned int x;
447 unsigned int y;
448
449 // merge the tracks into the sample buffer
450 quint8 *dst_buffer = &(m_write_buffer[0]);
451 unsigned int count = buf_len / (bytes_per_sample * tracks);
452 if (rest < count) count = Kwave::toUint(rest);
453
454 unsigned int written = 0;
455 for (written = 0; written < count; written++) {
456 const sample_t *src_buf = Q_NULLPTR;
457
458 // fill input buffer with samples
459 for (x = 0; x < tracks; ++x) {
460 in_samples[x] = 0;
461 Kwave::SampleReader *stream = src[x];
462 Q_ASSERT(stream);
463 if (!stream) continue;
464
465 if (!stream->eof()) (*stream) >> in_samples[x];
466 }
467
468 if (tracks > 2) {
469 // multiply matrix with input to get output
470 const Kwave::SampleArray &in = in_samples;
471 for (y = 0; y < out_tracks; ++y) {
472 double sum = 0;
473 for (x = 0; x < tracks; ++x)
474 sum += static_cast<double>(in[x]) * mixer[x][y];
475 out_samples[y] = static_cast<sample_t>(sum);
476 }
477
478 // use output of the matrix
479 src_buf = out_samples.constData();
480 } else {
481 // use input buffer directly
482 src_buf = in_samples.constData();
483 }
484
485 // sample conversion from 24bit to raw PCM, native endian
486 for (y = 0; y < out_tracks; ++y) {
487 sample_t s = *(src_buf++);
488 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
489 // big endian
490 if (bits >= 8)
491 *(dst_buffer++) = static_cast<quint8>(s >> 16);
492 if (bits > 8)
493 *(dst_buffer++) = static_cast<quint8>(s >> 8);
494 if (bits > 16)
495 *(dst_buffer++) = static_cast<quint8>(s & 0xFF);
496 if (bits > 24)
497 *(dst_buffer++) = 0x00;
498 #else
499 // little endian
500 if (bits > 24)
501 *(dst_buffer++) = 0x00;
502 if (bits > 16)
503 *(dst_buffer++) = static_cast<quint8>(s & 0xFF);
504 if (bits > 8)
505 *(dst_buffer++) = static_cast<quint8>(s >> 8);
506 if (bits >= 8)
507 *(dst_buffer++) = static_cast<quint8>(s >> 16);
508 #endif
509 }
510 }
511
512 // write out to the stdin of the external process
513 qint64 bytes_written = m_process.write(
514 reinterpret_cast<char *>(&(m_write_buffer[0])),
515 written * (bytes_per_sample * tracks)
516 );
517
518 // break if eof reached or disk full
519 if (!bytes_written) break;
520
521 // wait for write to take all data...
522 m_process.waitForBytesWritten();
523
524 // abort if the user pressed cancel
525 // --> this would leave a corrupted file !!!
526 if (src.isCanceled()) break;
527
528 Q_ASSERT(rest >= written);
529 rest -= written;
530 }
531
532 // flush and close the write channel
533 m_process.closeWriteChannel();
534
535 // wait until the process has finished
536 qDebug("wait for finish of the process");
537 while (m_process.state() != QProcess::NotRunning) {
538 m_process.waitForFinished(100);
539 if (src.isCanceled()) break;
540 }
541
542 int exit_code = m_process.exitCode();
543 qDebug("exit code=%d", exit_code);
544 if (!result || (exit_code != 0)) {
545 result = false;
546 stdError = QString::fromLocal8Bit(m_process.readAllStandardError());
547 qWarning("stderr output: %s", DBG(stdError));
548
549 Kwave::MessageBox::error(widget,
550 i18nc("%1=name of the external program, %2=stderr of the program",
551 "An error occurred while calling the external encoder '%1':\n\n%2",
552 m_program, stdError
553 ));
554 }
555
556 // if a ID3v1 tag is requested, the tag comes at the end
557 if (id3_tag_type != ID3TT_ID3V2)
558 id3_tag.Render(id3_writer, id3_tag_type);
559
560 {
561 QMutexLocker _lock(&m_lock);
562 m_dst = Q_NULLPTR;
563 dst.close();
564 }
565
566 return result;
567 }
568
569 /***************************************************************************/
dataAvailable()570 void Kwave::MP3Encoder::dataAvailable()
571 {
572 while (m_process.bytesAvailable()) {
573 qint64 len = m_process.read(&(m_read_buffer[0]), sizeof(m_read_buffer));
574 if (len) {
575 QMutexLocker _lock(&m_lock);
576 if (m_dst) m_dst->write(&(m_read_buffer[0]), len);
577 }
578 }
579 }
580
581 /***************************************************************************/
582 /***************************************************************************/
583