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