File indexing completed on 2024-05-12 04:49:59

0001 /*
0002     Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
0003     Copyright (C) 2005 Benjamin Meyer <ben at meyerhome dot net>
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Library General Public
0007     License as published by the Free Software Foundation; either
0008     version 2 of the License, or (at your option) any later version.
0009 
0010     This library is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013     Library General Public License for more details.
0014 
0015     You should have received a copy of the GNU Library General Public License
0016     along with this library; see the file COPYING.LIB.  If not, write to
0017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018     Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "encoderflac.h"
0022 
0023 #include <FLAC/format.h>
0024 #include <FLAC/metadata.h>
0025 #include <FLAC/stream_encoder.h>
0026 
0027 #if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
0028 #include "audiocd_flac_encoder.h"
0029 #endif
0030 
0031 #include "audiocd_kio_debug.h"
0032 #include <KConfig>
0033 
0034 #include <QDateTime>
0035 #include <QPair>
0036 
0037 Q_LOGGING_CATEGORY(AUDIOCD_KIO_LOG, "kf.kio.workers.audiocd")
0038 
0039 extern "C" {
0040 AUDIOCDPLUGINS_EXPORT void create_audiocd_encoders(KIO::WorkerBase *worker, QList<AudioCDEncoder *> &encoders)
0041 {
0042     encoders.append(new EncoderFLAC(worker));
0043 }
0044 }
0045 
0046 class EncoderFLAC::Private {
0047 
0048 public:
0049     FLAC__StreamEncoder *encoder;
0050     FLAC__StreamMetadata **metadata;
0051     KIO::WorkerBase *ioWorker;
0052     unsigned long data;
0053 #if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
0054     unsigned compression_level;
0055 #endif
0056 };
0057 
0058 static FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *encoder,
0059                                                     const FLAC__byte buffer[],
0060 #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
0061                                                     unsigned bytes,
0062 #else
0063                                                     size_t bytes,
0064 #endif
0065                                                     unsigned samples,
0066                                                     unsigned current_frame,
0067                                                     void *client_data)
0068 {
0069     Q_UNUSED(encoder)
0070     Q_UNUSED(samples)
0071     Q_UNUSED(current_frame)
0072 
0073     auto *d = (EncoderFLAC::Private *)client_data;
0074 
0075     d->data += bytes;
0076 
0077     QByteArray output;
0078 
0079     if (bytes) {
0080         output = QByteArray::fromRawData((const char *)buffer, bytes);
0081         d->ioWorker->data(output);
0082         output.clear();
0083     }
0084 
0085     return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
0086 }
0087 
0088 static void MetadataCallback(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data)
0089 {
0090     // We do not have seekable writeback so we just discard the updated metadata
0091     Q_UNUSED(encoder)
0092     Q_UNUSED(metadata)
0093     Q_UNUSED(client_data)
0094 }
0095 
0096 /*
0097 static FLAC__SeekableStreamEncoderSeekStatus  SeekCallback(const
0098 FLAC__SeekableStreamEncoder *encoder,  FLAC__uint64 absolute_byte_offset, void
0099 *client_data)
0100 {} ; */
0101 
0102 EncoderFLAC::EncoderFLAC(KIO::WorkerBase *worker)
0103     : AudioCDEncoder(worker)
0104 {
0105     d = new Private();
0106     d->ioWorker = worker;
0107     d->encoder = nullptr;
0108 #if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
0109     d->compression_level = 5;
0110 #endif
0111 }
0112 
0113 EncoderFLAC::~EncoderFLAC() {
0114     if (d->encoder)
0115         FLAC__stream_encoder_delete(d->encoder);
0116     delete d;
0117 }
0118 
0119 QWidget *EncoderFLAC::getConfigureWidget(KConfigSkeleton **manager) const
0120 {
0121 #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
0122     Q_UNUSED(manager);
0123     return NULL;
0124 #else
0125     (*manager) = Settings::self();
0126     return new EncoderFLACConfig();
0127 #endif
0128 }
0129 
0130 bool EncoderFLAC::init()
0131 {
0132     d->encoder = FLAC__stream_encoder_new();
0133     d->metadata = nullptr;
0134     d->data = 0;
0135     return true;
0136 }
0137 
0138 void EncoderFLAC::loadSettings() {
0139 #if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
0140     Settings *settings = Settings::self();
0141     d->compression_level = settings->flac_compression_level();
0142     if (d->compression_level > 8)
0143         d->compression_level = 5;
0144 #endif
0145 }
0146 
0147 // Estimate size to be 5/8 of uncompressed size.
0148 unsigned long EncoderFLAC::size(long time_secs) const {
0149     long uncompressed = (time_secs * (44100 * 2 * 2));
0150     return (uncompressed / 8) * 5 + 1000;
0151 }
0152 
0153 long EncoderFLAC::readInit(long size) {
0154     qCDebug(AUDIOCD_KIO_LOG) << "EncoderFLAC::readInit() called";
0155     d->data = 0;
0156 #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
0157     FLAC__stream_encoder_set_write_callback(d->encoder, WriteCallback);
0158     FLAC__stream_encoder_set_metadata_callback(d->encoder, MetadataCallback);
0159     FLAC__stream_encoder_set_client_data(d->encoder, d);
0160 #endif
0161 
0162 #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
0163     // The options match approximately those of flac compression-level-5
0164     FLAC__stream_encoder_set_do_mid_side_stereo(d->encoder, true);
0165     FLAC__stream_encoder_set_max_lpc_order(d->encoder, 8); // flac -l8
0166     FLAC__stream_encoder_set_min_residual_partition_order(d->encoder, 3);
0167     FLAC__stream_encoder_set_max_residual_partition_order(d->encoder,
0168                                                           3); // flac -r3,3
0169 #else
0170     FLAC__stream_encoder_set_compression_level(d->encoder, d->compression_level);
0171 #endif
0172 
0173     FLAC__stream_encoder_set_streamable_subset(d->encoder, true);
0174     if (size > 0)
0175         FLAC__stream_encoder_set_total_samples_estimate(d->encoder, size / 4);
0176 
0177 #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
0178     FLAC__stream_encoder_init(d->encoder);
0179 #else
0180     FLAC__stream_encoder_init_stream(d->encoder, WriteCallback, nullptr, nullptr, MetadataCallback, d);
0181 #endif
0182     return d->data;
0183 }
0184 
0185 long EncoderFLAC::read(qint16 *buf, int frames)
0186 {
0187     unsigned long olddata = d->data;
0188     auto buffer = new FLAC__int32[frames * 2];
0189     for (int i = 0; i < frames * 2; i++) {
0190         buffer[i] = (FLAC__int32)buf[i];
0191     }
0192 
0193     FLAC__stream_encoder_process_interleaved(d->encoder, buffer, frames);
0194     delete[] buffer;
0195     return d->data - olddata;
0196 }
0197 
0198 long EncoderFLAC::readCleanup()
0199 {
0200     FLAC__stream_encoder_finish(d->encoder);
0201     //    FLAC__stream_encoder_delete(d->encoder);
0202     if (d->metadata) {
0203         FLAC__metadata_object_delete(d->metadata[0]);
0204         delete[] d->metadata;
0205         d->metadata = nullptr;
0206     }
0207     return 0;
0208 }
0209 
0210 void EncoderFLAC::fillSongInfo(KCDDB::CDInfo info, int track, const QString &comment)
0211 {
0212     d->metadata = new FLAC__StreamMetadata *[1];
0213     d->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
0214     //    d->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
0215     //    d->metadata[2] =
0216     //    FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE)
0217 
0218     using Comment = QPair<QString, QVariant>;
0219     Comment comments[7] = {Comment(QLatin1String("TITLE"), info.track(track - 1).get(Title)),
0220                            Comment(QLatin1String("ARTIST"), info.track(track - 1).get(Artist)),
0221                            Comment(QLatin1String("ALBUM"), info.get(Title)),
0222                            Comment(QLatin1String("GENRE"), info.get(Genre)),
0223                            Comment(QLatin1String("TRACKNUMBER"), QString::number(track)),
0224                            Comment(QLatin1String("COMMENT"), comment),
0225                            Comment(QLatin1String("DATE"), QVariant(QString()))};
0226     if (info.get(Year).toInt() > 0) {
0227         const QDateTime dt = QDate(info.get(Year).toInt(), 1, 1).startOfDay();
0228         comments[6] = Comment(QStringLiteral("DATE"), dt.toString(Qt::ISODate));
0229     }
0230 
0231     FLAC__StreamMetadata_VorbisComment_Entry entry;
0232     QString field;
0233     int num_comments = 0;
0234 
0235     for (int i = 0; i < 7; i++) {
0236         if (!comments[i].second.toString().isEmpty()) {
0237             field = comments[i].first + QLatin1Char('=') + comments[i].second.toString();
0238             const QByteArray cfield = field.toUtf8();
0239             entry.entry = (FLAC__byte *)qstrdup(cfield.data());
0240             entry.length = cfield.length();
0241             // Insert in vorbiscomment and assign ownership of pointers to FLAC
0242             FLAC__metadata_object_vorbiscomment_insert_comment(d->metadata[0], num_comments, entry, false);
0243             num_comments++;
0244         }
0245     }
0246 
0247     FLAC__stream_encoder_set_metadata(d->encoder, d->metadata, 1);
0248 }