File indexing completed on 2024-05-12 04:50:00
0001 /* 0002 Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org> 0003 Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org> 0004 Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de> 0005 Copyright (C) 2001 Adrian Schroeter <adrian@suse.de> 0006 Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se> 0007 Copyright (C) 2003 Scott Wheeler <wheeler@kde.org> 0008 Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net> 0009 0010 This program is free software; you can redistribute it and/or modify 0011 it under the terms of the GNU General Public License as published by 0012 the Free Software Foundation; either version 2 of the License, or 0013 (at your option) any later version. 0014 0015 This program is distributed in the hope that it will be useful, 0016 but WITHOUT ANY WARRANTY; without even the implied warranty of 0017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0018 GNU General Public License for more details. 0019 0020 You should have received a copy of the GNU General Public License 0021 along with this program; if not, write to the Free Software 0022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 0023 USA. 0024 */ 0025 0026 #include "encodervorbis.h" 0027 #include "audiocd_vorbis_encoder.h" 0028 0029 #include <KConfig> 0030 #include <QByteArray> 0031 #include <QDateTime> 0032 #include <QRandomGenerator> 0033 #include <cstdlib> 0034 #include <ctime> 0035 #include <vorbis/vorbisenc.h> 0036 0037 extern "C" { 0038 AUDIOCDPLUGINS_EXPORT void create_audiocd_encoders(KIO::WorkerBase *worker, QList<AudioCDEncoder *> &encoders) 0039 { 0040 encoders.append(new EncoderVorbis(worker)); 0041 } 0042 } 0043 0044 // these are the approx. bitrates for the current 5 Vorbis modes 0045 static const int vorbis_nominal_bitrates[] = {128, 160, 192, 256, 350}; 0046 static const int vorbis_bitrates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 350}; 0047 0048 class EncoderVorbis::Private { 0049 0050 public: 0051 ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ 0052 ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ 0053 ogg_packet op; /* one raw packet of data for decode */ 0054 0055 vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ 0056 vorbis_comment vc; /* struct that stores all the user comments */ 0057 0058 vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ 0059 vorbis_block vb; /* local working space for packet->PCM decode */ 0060 bool write_vorbis_comments; 0061 long vorbis_bitrate_lower; 0062 long vorbis_bitrate_upper; 0063 long vorbis_bitrate_nominal; 0064 int vorbis_encode_method; 0065 double vorbis_quality; 0066 int vorbis_bitrate; 0067 }; 0068 0069 EncoderVorbis::EncoderVorbis(KIO::WorkerBase *worker) 0070 : AudioCDEncoder(worker) 0071 { 0072 d = new Private(); 0073 } 0074 0075 EncoderVorbis::~EncoderVorbis() 0076 { 0077 vorbis_info_clear(&d->vi); 0078 vorbis_comment_clear(&d->vc); 0079 delete d; 0080 } 0081 0082 QWidget *EncoderVorbis::getConfigureWidget(KConfigSkeleton **manager) const 0083 { 0084 (*manager) = Settings::self(); 0085 auto config = new EncoderVorbisConfig(); 0086 config->kcfg_vorbis_quality->setRange(0.0, 10.0); 0087 config->kcfg_vorbis_quality->setSingleStep(0.1); 0088 config->vorbis_bitrate_settings->hide(); 0089 return config; 0090 } 0091 0092 bool EncoderVorbis::init() 0093 { 0094 vorbis_info_init(&d->vi); 0095 vorbis_comment_init(&d->vc); 0096 0097 vorbis_comment_add_tag(&d->vc, const_cast<char *>("kde-encoder"), const_cast<char *>("kio_audiocd")); 0098 return true; 0099 } 0100 0101 void EncoderVorbis::loadSettings() 0102 { 0103 Settings *settings = Settings::self(); 0104 0105 d->vorbis_encode_method = settings->vorbis_enc_method(); 0106 d->vorbis_quality = settings->vorbis_quality(); 0107 0108 if (settings->set_vorbis_min_br()) { 0109 d->vorbis_bitrate_lower = vorbis_bitrates[settings->vorbis_min_br()] * 1000; 0110 } else { 0111 d->vorbis_bitrate_lower = -1; 0112 } 0113 0114 if (settings->set_vorbis_max_br()) { 0115 d->vorbis_bitrate_upper = vorbis_bitrates[settings->vorbis_max_br()] * 1000; 0116 } else { 0117 d->vorbis_bitrate_upper = -1; 0118 } 0119 0120 // this is such a hack! 0121 if (d->vorbis_bitrate_upper != -1 && d->vorbis_bitrate_lower != -1) { 0122 d->vorbis_bitrate = 104000; // empirically determined ...?! 0123 } else { 0124 d->vorbis_bitrate = 160 * 1000; 0125 } 0126 0127 if (settings->set_vorbis_nominal_br()) { 0128 d->vorbis_bitrate_nominal = vorbis_nominal_bitrates[settings->vorbis_nominal_br()] * 1000; 0129 d->vorbis_bitrate = d->vorbis_bitrate_nominal; 0130 } else { 0131 d->vorbis_bitrate_nominal = -1; 0132 } 0133 0134 d->write_vorbis_comments = settings->vorbis_comments(); 0135 0136 // Now that we have read in the settings apply them to the encoder lib 0137 switch (d->vorbis_encode_method) { 0138 case 0: 0139 vorbis_encode_init_vbr(&d->vi, 2, 44100, d->vorbis_quality / 10.0); 0140 break; 0141 case 1: 0142 vorbis_encode_init(&d->vi, 2, 44100, d->vorbis_bitrate_upper, d->vorbis_bitrate_nominal, d->vorbis_bitrate_lower); 0143 break; 0144 } 0145 } 0146 0147 long EncoderVorbis::flush_vorbis() 0148 { 0149 long processed(0); 0150 0151 while (vorbis_analysis_blockout(&d->vd, &d->vb) == 1) { 0152 vorbis_analysis(&d->vb, nullptr); 0153 /* Non-ancient case. */ 0154 vorbis_bitrate_addblock(&d->vb); 0155 0156 while (vorbis_bitrate_flushpacket(&d->vd, &d->op)) { 0157 ogg_stream_packetin(&d->os, &d->op); 0158 while (int result = ogg_stream_pageout(&d->os, &d->og)) { 0159 if (!result) 0160 break; 0161 0162 char *oggheader = reinterpret_cast<char *>(d->og.header); 0163 char *oggbody = reinterpret_cast<char *>(d->og.body); 0164 0165 if (d->og.header_len) { 0166 ioWorker->data(QByteArray::fromRawData(oggheader, d->og.header_len)); 0167 } 0168 0169 if (d->og.body_len) { 0170 ioWorker->data(QByteArray::fromRawData(oggbody, d->og.body_len)); 0171 } 0172 processed += d->og.header_len + d->og.body_len; 0173 } 0174 } 0175 } 0176 return processed; 0177 } 0178 0179 unsigned long EncoderVorbis::size(long time_secs) const { 0180 long vorbis_size; 0181 switch (d->vorbis_encode_method) { 0182 case 0: // quality based encoding 0183 { 0184 // Estimated numbers based on the Vorbis FAQ: 0185 // https://xiph.org/vorbis/faq/#quality 0186 0187 static long vorbis_q_bitrate[] = {60, 74, 86, 106, 120, 152, 183, 207, 239, 309, 440}; 0188 long quality = static_cast<long>(d->vorbis_quality); 0189 if (quality < 0 || quality > 10) 0190 quality = 3; 0191 vorbis_size = (time_secs * vorbis_q_bitrate[quality] * 1000) / 8; 0192 0193 break; 0194 } 0195 default: // bitrate based encoding 0196 vorbis_size = (time_secs * d->vorbis_bitrate / 8); 0197 break; 0198 } 0199 0200 return vorbis_size; 0201 } 0202 0203 const char *EncoderVorbis::mimeType() const 0204 { 0205 return "audio/x-vorbis+ogg"; 0206 } 0207 0208 long EncoderVorbis::readInit(long /*size*/) 0209 { 0210 ogg_packet header; 0211 ogg_packet header_comm; 0212 ogg_packet header_code; 0213 0214 vorbis_analysis_init(&d->vd, &d->vi); 0215 vorbis_block_init(&d->vd, &d->vb); 0216 0217 ogg_stream_init(&d->os, QRandomGenerator::global()->generate()); 0218 0219 vorbis_analysis_headerout(&d->vd, &d->vc, &header, &header_comm, &header_code); 0220 0221 ogg_stream_packetin(&d->os, &header); 0222 ogg_stream_packetin(&d->os, &header_comm); 0223 ogg_stream_packetin(&d->os, &header_code); 0224 0225 while (int result = ogg_stream_flush(&d->os, &d->og)) { 0226 if (!result) 0227 break; 0228 0229 char *oggheader = reinterpret_cast<char *>(d->og.header); 0230 char *oggbody = reinterpret_cast<char *>(d->og.body); 0231 0232 if (d->og.header_len) { 0233 ioWorker->data(QByteArray::fromRawData(oggheader, d->og.header_len)); 0234 } 0235 0236 if (d->og.body_len) { 0237 ioWorker->data(QByteArray::fromRawData(oggbody, d->og.body_len)); 0238 } 0239 } 0240 return 0; 0241 } 0242 0243 long EncoderVorbis::read(qint16 *buf, int frames) 0244 { 0245 int i; 0246 float **buffer = vorbis_analysis_buffer(&d->vd, frames); 0247 0248 /* uninterleave samples */ 0249 for (i = 0; i < frames; i++) { 0250 buffer[0][i] = buf[2 * i] / 32768.0; 0251 buffer[1][i] = buf[2 * i + 1] / 32768.0; 0252 } 0253 0254 /* process chunk of data */ 0255 vorbis_analysis_wrote(&d->vd, i); 0256 return flush_vorbis(); 0257 } 0258 0259 long EncoderVorbis::readCleanup() 0260 { 0261 // send end-of-stream and flush the encoder 0262 vorbis_analysis_wrote(&d->vd, 0); 0263 long processed = flush_vorbis(); 0264 ogg_stream_clear(&d->os); 0265 vorbis_block_clear(&d->vb); 0266 vorbis_dsp_clear(&d->vd); 0267 vorbis_info_clear(&d->vi); 0268 return processed; 0269 } 0270 0271 void EncoderVorbis::fillSongInfo(KCDDB::CDInfo info, int track, const QString &comment) 0272 { 0273 if (!d->write_vorbis_comments) 0274 return; 0275 0276 using CommentField = QPair<QByteArray, QVariant>; 0277 QList<CommentField> commentFields; 0278 0279 commentFields.append(CommentField("TITLE", info.track(track - 1).get(Title))); 0280 commentFields.append(CommentField("ARTIST", info.track(track - 1).get(Artist))); 0281 commentFields.append(CommentField("ALBUM", info.get(Title))); 0282 commentFields.append(CommentField("GENRE", info.get(Genre))); 0283 commentFields.append(CommentField("TRACKNUMBER", QString::number(track))); 0284 commentFields.append(CommentField("COMMENT", comment)); 0285 0286 if (info.get(Year).toInt() > 0) { 0287 const QDateTime dt = QDate(info.get(Year).toInt(), 1, 1).startOfDay(); 0288 commentFields.append(CommentField("DATE", QLatin1String(dt.toString(Qt::ISODate).toUtf8().data()))); 0289 } 0290 0291 for (QList<CommentField>::iterator it = commentFields.begin(); it != commentFields.end(); ++it) { 0292 // if the value is not empty 0293 if (!(*it).second.toString().isEmpty()) { 0294 char *key = qstrdup((*it).first.constData()); 0295 char *value = qstrdup((*it).second.toString().toUtf8().data()); 0296 0297 vorbis_comment_add_tag(&d->vc, key, value); 0298 0299 delete[] key; 0300 delete[] value; 0301 } 0302 } 0303 }