File indexing completed on 2024-05-12 04:51:34
0001 /* 0002 SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl> 0003 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "k3boggvorbisencoder.h" 0009 #include "k3boggvorbisencoderdefaults.h" 0010 #include "k3bcore.h" 0011 #include "k3bplugin_i18n.h" 0012 #include <config-k3b.h> 0013 0014 #include <KConfig> 0015 #include <KConfigGroup> 0016 #include <KSharedConfig> 0017 0018 #include <QDebug> 0019 0020 #include <vorbis/vorbisenc.h> 0021 0022 // for the random generator 0023 #include <stdlib.h> 0024 #include <time.h> 0025 0026 0027 K_PLUGIN_CLASS_WITH_JSON(K3bOggVorbisEncoder, "k3boggvorbisencoder.json") 0028 0029 0030 // quality levels -1 to 10 map to 0 to 11 0031 static const int s_rough_average_quality_level_bitrates[] = { 0032 45, 0033 64, 0034 80, 0035 96, 0036 112, 0037 128, 0038 160, 0039 192, 0040 224, 0041 256, 0042 320, 0043 400 0044 }; 0045 0046 // quality levels -1 to 10 map to 0 to 11 0047 // static const char* s_ogg_quality_level_strings[] = { 0048 // I18N_NOOP("Low quality"), 0049 // I18N_NOOP(""), 0050 // I18N_NOOP(""), 0051 // I18N_NOOP(""), 0052 // I18N_NOOP(""), 0053 // I18N_NOOP("targetted %1 kbps"), 0054 // I18N_NOOP("targetted %1 kbps"), 0055 // I18N_NOOP("targetted %1 kbps"), 0056 // I18N_NOOP(""), 0057 // I18N_NOOP(""), 0058 // I18N_NOOP(""), 0059 // I18N_NOOP(""), 0060 // }; 0061 0062 0063 // THIS IS BASED ON THE OGG VORBIS LIB EXAMPLE 0064 // BECAUSE OF THE LACK OF DOCUMENTATION 0065 0066 0067 class K3bOggVorbisEncoder::Private 0068 { 0069 public: 0070 Private() 0071 : manualBitrate(false), 0072 qualityLevel(4), 0073 bitrateUpper(-1), 0074 bitrateNominal(-1), 0075 bitrateLower(-1), 0076 // sampleRate(44100), 0077 oggStream(0), 0078 oggPage(0), 0079 oggPacket(0), 0080 vorbisInfo(0), 0081 vorbisComment(0), 0082 vorbisDspState(0), 0083 vorbisBlock(0), 0084 headersWritten(false) { 0085 } 0086 0087 // encoding settings 0088 bool manualBitrate; 0089 // 0 to 10 -> 0.0 - 1.0 0090 int qualityLevel; 0091 int bitrateUpper; 0092 int bitrateNominal; 0093 int bitrateLower; 0094 // int sampleRate; 0095 0096 // encoding structures 0097 ogg_stream_state *oggStream; // take physical pages, weld into a logical stream of packets 0098 ogg_page *oggPage; // one Ogg bitstream page. Vorbis packets are inside 0099 ogg_packet *oggPacket; // one raw packet of data for decode 0100 vorbis_info *vorbisInfo; // struct that stores all the static vorbis bitstream settings 0101 vorbis_comment *vorbisComment; // struct that stores all the user comments 0102 vorbis_dsp_state *vorbisDspState; // central working state for the packet->PCM decoder 0103 vorbis_block *vorbisBlock; // local working space for packet->PCM decode 0104 0105 bool headersWritten; 0106 }; 0107 0108 0109 K3bOggVorbisEncoder::K3bOggVorbisEncoder( QObject* parent, const QVariantList& ) 0110 : K3b::AudioEncoder( parent ) 0111 { 0112 d = new Private(); 0113 } 0114 0115 0116 K3bOggVorbisEncoder::~K3bOggVorbisEncoder() 0117 { 0118 cleanup(); 0119 delete d; 0120 } 0121 0122 0123 bool K3bOggVorbisEncoder::initEncoderInternal( const QString&, const K3b::Msf& /*length*/, const MetaData& metaData ) 0124 { 0125 cleanup(); 0126 0127 // load user settings 0128 loadConfig(); 0129 0130 d->oggPage = new ogg_page; 0131 d->oggPacket = new ogg_packet; 0132 d->vorbisInfo = new vorbis_info; 0133 0134 vorbis_info_init( d->vorbisInfo ); 0135 0136 int ret = 0; 0137 0138 if( d->manualBitrate ) { 0139 qDebug() << "(K3bOggVorbisEncoder) calling: " 0140 << "vorbis_encode_init( d->vorbisInfo, 2, 44100, " 0141 << (d->bitrateUpper != -1 ? d->bitrateUpper*1000 : -1) << ", " 0142 << (d->bitrateNominal != -1 ? d->bitrateNominal*1000 : -1) << ", " 0143 << (d->bitrateLower != -1 ? d->bitrateLower*1000 : -1) << " );" << Qt::endl; 0144 0145 ret = vorbis_encode_init( d->vorbisInfo, 0146 2, // 2 channels: stereo 0147 44100, 0148 d->bitrateUpper != -1 ? d->bitrateUpper*1000 : -1, 0149 d->bitrateNominal != -1 ? d->bitrateNominal*1000 : -1, 0150 d->bitrateLower != -1 ? d->bitrateLower*1000 : -1 ); 0151 } 0152 else { 0153 if( d->qualityLevel < -1 ) 0154 d->qualityLevel = -1; 0155 else if( d->qualityLevel > 10 ) 0156 d->qualityLevel = 10; 0157 0158 qDebug() << "(K3bOggVorbisEncoder) calling: " 0159 << "vorbis_encode_init_vbr( d->vorbisInfo, 2, 44100, " 0160 << (float)d->qualityLevel/10.0 << ");" << Qt::endl; 0161 0162 ret = vorbis_encode_init_vbr( d->vorbisInfo, 0163 2, // 2 channels: stereo 0164 44100, 0165 (float)d->qualityLevel/10.0 ); 0166 } 0167 0168 if( ret ) { 0169 qDebug() << "(K3bOggVorbisEncoder) vorbis_encode_init failed: " << ret; 0170 cleanup(); 0171 return false; 0172 } 0173 0174 // init the comment stuff 0175 d->vorbisComment = new vorbis_comment; 0176 vorbis_comment_init( d->vorbisComment ); 0177 0178 // add the encoder tag (so everybody knows we did it! ;) 0179 vorbis_comment_add_tag( d->vorbisComment, QByteArray("ENCODER").data(), QByteArray("K3bOggVorbisEncoderPlugin").data() ); 0180 0181 // set up the analysis state and auxiliary encoding storage 0182 d->vorbisDspState = new vorbis_dsp_state; 0183 d->vorbisBlock = new vorbis_block; 0184 vorbis_analysis_init( d->vorbisDspState, d->vorbisInfo ); 0185 vorbis_block_init( d->vorbisDspState, d->vorbisBlock ); 0186 0187 // set up our packet->stream encoder 0188 // pick a random serial number; that way we can more likely build 0189 // chained streams just by concatenation 0190 d->oggStream = new ogg_stream_state; 0191 srand( time(0) ); 0192 ogg_stream_init( d->oggStream, rand() ); 0193 0194 // Set meta data 0195 for( MetaData::const_iterator it = metaData.constBegin(); it != metaData.constEnd(); ++it ) { 0196 QByteArray key; 0197 0198 switch( it.key() ) { 0199 case META_TRACK_TITLE: 0200 key = "TITLE"; 0201 break; 0202 case META_TRACK_ARTIST: 0203 key = "ARTIST"; 0204 break; 0205 case META_ALBUM_TITLE: 0206 key = "ALBUM"; 0207 break; 0208 case META_ALBUM_COMMENT: 0209 key = "DESCRIPTION"; 0210 break; 0211 case META_YEAR: 0212 key = "DATE"; 0213 break; 0214 case META_TRACK_NUMBER: 0215 key = "TRACKNUMBER"; 0216 break; 0217 case META_GENRE: 0218 key = "GENRE"; 0219 break; 0220 default: 0221 break; 0222 } 0223 0224 if( !key.isEmpty() ) { 0225 vorbis_comment_add_tag( d->vorbisComment, key.data(), it.value().toString().toUtf8().data() ); 0226 } 0227 } 0228 0229 return true; 0230 } 0231 0232 0233 bool K3bOggVorbisEncoder::writeOggHeaders() 0234 { 0235 if( !d->oggStream ) { 0236 qDebug() << "(K3bOggVorbisEncoder) call to writeOggHeaders without init."; 0237 return false; 0238 } 0239 if( d->headersWritten ) { 0240 qDebug() << "(K3bOggVorbisEncoder) headers already written."; 0241 return true; 0242 } 0243 0244 // 0245 // Vorbis streams begin with three headers; the initial header (with 0246 // most of the codec setup parameters) which is mandated by the Ogg 0247 // bitstream spec. The second header holds any comment fields. The 0248 // third header holds the bitstream codebook. We merely need to 0249 // make the headers, then pass them to libvorbis one at a time; 0250 // libvorbis handles the additional Ogg bitstream constraints 0251 // 0252 ogg_packet header; 0253 ogg_packet header_comm; 0254 ogg_packet header_code; 0255 0256 vorbis_analysis_headerout( d->vorbisDspState, 0257 d->vorbisComment, 0258 &header, 0259 &header_comm, 0260 &header_code); 0261 0262 // automatically placed in its own page 0263 ogg_stream_packetin( d->oggStream, &header ); 0264 ogg_stream_packetin( d->oggStream, &header_comm ); 0265 ogg_stream_packetin( d->oggStream, &header_code ); 0266 0267 // 0268 // This ensures the actual 0269 // audio data will start on a new page, as per spec 0270 // 0271 QByteArray data; 0272 while( ogg_stream_flush( d->oggStream, d->oggPage ) ) { 0273 writeData( (char*)d->oggPage->header, d->oggPage->header_len ); 0274 writeData( (char*)d->oggPage->body, d->oggPage->body_len ); 0275 } 0276 0277 d->headersWritten = true; 0278 0279 return true; 0280 } 0281 0282 0283 qint64 K3bOggVorbisEncoder::encodeInternal( const char* data, qint64 len ) 0284 { 0285 if( !d->headersWritten ) 0286 if( !writeOggHeaders() ) 0287 return -1; 0288 0289 // expose the buffer to submit data 0290 float** buffer = vorbis_analysis_buffer( d->vorbisDspState, len/4 ); 0291 0292 // uninterleave samples 0293 qint64 i = 0; 0294 for( i = 0; i < len/4; ++i ) { 0295 buffer[0][i]=( (data[i*4+1]<<8) | (0x00ff&(int)data[i*4]) ) / 32768.f; 0296 buffer[1][i]=( (data[i*4+3]<<8) | (0x00ff&(int)data[i*4+2]) ) / 32768.f; 0297 } 0298 0299 // tell the library how much we actually submitted 0300 vorbis_analysis_wrote( d->vorbisDspState, i ); 0301 0302 return flushVorbis(); 0303 } 0304 0305 0306 long K3bOggVorbisEncoder::flushVorbis() 0307 { 0308 // vorbis does some data preanalysis, then divvies up blocks for 0309 // more involved (potentially parallel) processing. Get a single 0310 // block for encoding now 0311 long writtenData = 0; 0312 while( vorbis_analysis_blockout( d->vorbisDspState, d->vorbisBlock ) == 1 ) { 0313 0314 // analysis 0315 vorbis_analysis( d->vorbisBlock, 0 ); 0316 vorbis_bitrate_addblock( d->vorbisBlock ); 0317 0318 while( vorbis_bitrate_flushpacket( d->vorbisDspState, d->oggPacket ) ) { 0319 0320 // weld the packet into the bitstream 0321 ogg_stream_packetin( d->oggStream, d->oggPacket ); 0322 0323 // write out pages (if any) 0324 while( ogg_stream_pageout( d->oggStream, d->oggPage ) ) { 0325 writeData( (char*)d->oggPage->header, d->oggPage->header_len ); 0326 writeData( (char*)d->oggPage->body, d->oggPage->body_len ); 0327 0328 writtenData += ( d->oggPage->header_len + d->oggPage->body_len ); 0329 } 0330 } 0331 } 0332 0333 return writtenData; 0334 } 0335 0336 0337 void K3bOggVorbisEncoder::finishEncoderInternal() 0338 { 0339 if( d->vorbisDspState ) { 0340 vorbis_analysis_wrote( d->vorbisDspState, 0 ); 0341 flushVorbis(); 0342 } 0343 else 0344 qDebug() << "(K3bOggVorbisEncoder) call to finishEncoderInternal without init."; 0345 } 0346 0347 0348 void K3bOggVorbisEncoder::cleanup() 0349 { 0350 if( d->oggStream ) { 0351 ogg_stream_clear( d->oggStream ); 0352 delete d->oggStream; 0353 d->oggStream = 0; 0354 } 0355 if( d->vorbisBlock ) { 0356 vorbis_block_clear( d->vorbisBlock ); 0357 delete d->vorbisBlock; 0358 d->vorbisBlock = 0; 0359 } 0360 if( d->vorbisDspState ) { 0361 vorbis_dsp_clear( d->vorbisDspState ); 0362 delete d->vorbisDspState; 0363 d->vorbisDspState = 0; 0364 } 0365 if( d->vorbisComment ) { 0366 vorbis_comment_clear( d->vorbisComment ); 0367 delete d->vorbisComment; 0368 d->vorbisComment = 0; 0369 } 0370 if( d->vorbisInfo ) { 0371 vorbis_info_clear( d->vorbisInfo ); 0372 delete d->vorbisInfo; 0373 d->vorbisInfo = 0; 0374 } 0375 0376 // ogg_page and ogg_packet structs always point to storage in 0377 // libvorbis. They're never freed or manipulated directly 0378 if( d->oggPage ) { 0379 delete d->oggPage; 0380 d->oggPage = 0; 0381 } 0382 if( d->oggPacket ) { 0383 delete d->oggPacket; 0384 d->oggPacket = 0; 0385 } 0386 0387 d->headersWritten = false; 0388 } 0389 0390 0391 void K3bOggVorbisEncoder::loadConfig() 0392 { 0393 KSharedConfig::Ptr c = KSharedConfig::openConfig(); 0394 KConfigGroup grp(c, QStringLiteral("K3bOggVorbisEncoderPlugin") ); 0395 0396 d->manualBitrate = grp.readEntry( "manual bitrate", DEFAULT_MANUAL_BITRATE ); 0397 d->qualityLevel = grp.readEntry( "quality level", DEFAULT_QUALITY_LEVEL ); 0398 d->bitrateUpper = grp.readEntry( "bitrate upper", DEFAULT_BITRATE_UPPER ); 0399 d->bitrateNominal = grp.readEntry( "bitrate nominal", DEFAULT_BITRATE_NOMINAL ); 0400 d->bitrateLower = grp.readEntry( "bitrate lower", DEFAULT_BITRATE_LOWER ); 0401 // d->sampleRate = c->readEntry( "samplerate", DEFAULT_SAMPLERATE ); 0402 } 0403 0404 0405 QString K3bOggVorbisEncoder::fileTypeComment( const QString& ) const 0406 { 0407 return i18n("Ogg-Vorbis"); 0408 } 0409 0410 0411 long long K3bOggVorbisEncoder::fileSize( const QString&, const K3b::Msf& msf ) const 0412 { 0413 KSharedConfig::Ptr c = KSharedConfig::openConfig(); 0414 KConfigGroup grp(c, QStringLiteral("K3bOggVorbisEncoderPlugin") ); 0415 0416 // the following code is based on the size estimation from the audiocd KIO worker 0417 // TODO: reimplement. 0418 0419 if( !grp.readEntry( "manual bitrate", DEFAULT_MANUAL_BITRATE ) ) { 0420 // Estimated numbers based on the Vorbis FAQ: 0421 // https://xiph.org/vorbis/faq/#quality 0422 0423 // static long vorbis_q_bitrate[] = { 45, 60, 74, 86, 106, 120, 152, 0424 // 183, 207, 239, 309, 440 }; 0425 0426 int qualityLevel = grp.readEntry( "quality level", DEFAULT_QUALITY_LEVEL ); 0427 0428 if( qualityLevel < -1 ) 0429 qualityLevel = -1; 0430 else if( qualityLevel > 10 ) 0431 qualityLevel = 10; 0432 return ( (msf.totalFrames()/75) * s_rough_average_quality_level_bitrates[qualityLevel+1] * 1000 ) / 8; 0433 } 0434 else { 0435 return (msf.totalFrames()/75) * grp.readEntry( "bitrate nominal", 160 ) * 1000 / 8; 0436 } 0437 } 0438 0439 #include "k3boggvorbisencoder.moc" 0440 0441 #include "moc_k3boggvorbisencoder.cpp"