You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
337 lines
9.8 KiB
C++
337 lines
9.8 KiB
C++
/*
|
|
Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
|
|
Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
|
|
Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
|
|
Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
|
|
Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
|
|
Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
|
|
Copyright (C) 2004, 2005 Benjamin Meyer <ben at meyerhome dot net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "encodervorbis.h"
|
|
#include "audiocd_vorbis_encoder.h"
|
|
#include "encodervorbisconfig.h"
|
|
|
|
#ifdef HAVE_VORBIS
|
|
|
|
#include <vorbis/vorbisenc.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <tdeconfig.h>
|
|
#include <knuminput.h>
|
|
#include <tqgroupbox.h>
|
|
|
|
#include <tdeglobal.h>
|
|
#include <tdelocale.h>
|
|
|
|
extern "C"
|
|
{
|
|
KDE_EXPORT void create_audiocd_encoders(TDEIO::SlaveBase *slave, TQPtrList<AudioCDEncoder> &encoders)
|
|
{
|
|
encoders.append(new EncoderVorbis(slave));
|
|
}
|
|
}
|
|
|
|
// these are the approx. bitrates for the current 5 Vorbis modes
|
|
static int vorbis_nominal_bitrates[] = { 128, 160, 192, 256, 350 };
|
|
static int vorbis_bitrates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 350 };
|
|
|
|
class EncoderVorbis::Private {
|
|
|
|
public:
|
|
ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
|
|
ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
|
|
ogg_packet op; /* one raw packet of data for decode */
|
|
|
|
vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
|
|
vorbis_comment vc; /* struct that stores all the user comments */
|
|
|
|
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
|
|
vorbis_block vb; /* local working space for packet->PCM decode */
|
|
bool write_vorbis_comments;
|
|
long vorbis_bitrate_lower;
|
|
long vorbis_bitrate_upper;
|
|
long vorbis_bitrate_nominal;
|
|
int vorbis_encode_method;
|
|
double vorbis_quality;
|
|
int vorbis_bitrate;
|
|
};
|
|
|
|
EncoderVorbis::EncoderVorbis(TDEIO::SlaveBase *slave) : AudioCDEncoder(slave) {
|
|
d = new Private();
|
|
}
|
|
|
|
EncoderVorbis::~EncoderVorbis(){
|
|
delete d;
|
|
}
|
|
|
|
TQWidget* EncoderVorbis::getConfigureWidget(TDEConfigSkeleton** manager) const {
|
|
(*manager) = Settings::self();
|
|
TDEGlobal::locale()->insertCatalogue("audiocd_encoder_vorbis");
|
|
EncoderVorbisConfig *config = new EncoderVorbisConfig();
|
|
config->kcfg_vorbis_quality->setRange(0.0, 10.0, 0.2, true);
|
|
config->vorbis_bitrate_settings->hide();
|
|
return config;
|
|
}
|
|
|
|
bool EncoderVorbis::init(){
|
|
vorbis_info_init(&d->vi);
|
|
vorbis_comment_init(&d->vc);
|
|
|
|
vorbis_comment_add_tag
|
|
(
|
|
&d->vc,
|
|
const_cast<char *>("kde-encoder"),
|
|
const_cast<char *>("tdeio_audiocd")
|
|
);
|
|
return true;
|
|
}
|
|
|
|
void EncoderVorbis::loadSettings(){
|
|
Settings *settings = Settings::self();
|
|
|
|
d->vorbis_encode_method = settings->vorbis_enc_method();
|
|
d->vorbis_quality = settings->vorbis_quality();
|
|
|
|
if ( settings->set_vorbis_min_br()) {
|
|
d->vorbis_bitrate_lower = vorbis_bitrates[settings->vorbis_min_br()] * 1000;
|
|
} else {
|
|
d->vorbis_bitrate_lower = -1;
|
|
}
|
|
|
|
if ( settings->set_vorbis_max_br() ) {
|
|
d->vorbis_bitrate_upper = vorbis_bitrates[settings->vorbis_max_br()] * 1000;
|
|
} else {
|
|
d->vorbis_bitrate_upper = -1;
|
|
}
|
|
|
|
// this is such a hack!
|
|
if ( d->vorbis_bitrate_upper != -1 && d->vorbis_bitrate_lower != -1 ) {
|
|
d->vorbis_bitrate = 104000; // empirically determined ...?!
|
|
} else {
|
|
d->vorbis_bitrate = 160 * 1000;
|
|
}
|
|
|
|
if ( settings->set_vorbis_nominal_br() ) {
|
|
d->vorbis_bitrate_nominal = vorbis_nominal_bitrates[settings->vorbis_nominal_br()] * 1000;
|
|
d->vorbis_bitrate = d->vorbis_bitrate_nominal;
|
|
} else {
|
|
d->vorbis_bitrate_nominal = -1;
|
|
}
|
|
|
|
d->write_vorbis_comments = settings->vorbis_comments();
|
|
|
|
// Now that we have read in the settings apply them to the encoder lib
|
|
switch (d->vorbis_encode_method) {
|
|
case 0:
|
|
/* Support very old libvorbis by simply falling through. */
|
|
#if HAVE_VORBIS >= 2
|
|
vorbis_encode_init_vbr(&d->vi, 2, 44100, d->vorbis_quality/10.0);
|
|
break;
|
|
#endif
|
|
case 1:
|
|
vorbis_encode_init(&d->vi, 2, 44100, d->vorbis_bitrate_upper, d->vorbis_bitrate_nominal, d->vorbis_bitrate_lower);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
long EncoderVorbis::flush_vorbis(void) {
|
|
long processed(0);
|
|
|
|
while(vorbis_analysis_blockout(&d->vd,&d->vb)==1) {
|
|
/* Support ancient libvorbis (< RC3). */
|
|
#if HAVE_VORBIS >= 2
|
|
vorbis_analysis(&d->vb,NULL);
|
|
/* Non-ancient case. */
|
|
vorbis_bitrate_addblock(&d->vb);
|
|
|
|
while(vorbis_bitrate_flushpacket(&d->vd, &d->op)) {
|
|
#else
|
|
vorbis_analysis(&d->vb,&d->op);
|
|
/* Make a lexical block to place the #ifdef's nearby. */
|
|
if (1) {
|
|
#endif
|
|
ogg_stream_packetin(&d->os,&d->op);
|
|
while(int result=ogg_stream_pageout(&d->os,&d->og)) {
|
|
if (!result) break;
|
|
|
|
TQByteArray output;
|
|
|
|
char * oggheader = reinterpret_cast<char *>(d->og.header);
|
|
char * oggbody = reinterpret_cast<char *>(d->og.body);
|
|
|
|
if (d->og.header_len) {
|
|
output.setRawData(oggheader, d->og.header_len);
|
|
ioslave->data(output);
|
|
output.resetRawData(oggheader, d->og.header_len);
|
|
}
|
|
|
|
if (d->og.body_len) {
|
|
output.setRawData(oggbody, d->og.body_len);
|
|
ioslave->data(output);
|
|
output.resetRawData(oggbody, d->og.body_len);
|
|
}
|
|
processed += d->og.header_len + d->og.body_len;
|
|
}
|
|
}
|
|
}
|
|
return processed;
|
|
}
|
|
|
|
unsigned long EncoderVorbis::size(long time_secs) const {
|
|
long vorbis_size;
|
|
switch (d->vorbis_encode_method)
|
|
{
|
|
case 0: // quality based encoding
|
|
|
|
#if HAVE_VORBIS >= 2 // If really old Vorbis is being used, skip this nicely.
|
|
|
|
{
|
|
// Estimated numbers based on the Vorbis FAQ:
|
|
// http://www.xiph.org/archives/vorbis-faq/200203/0030.html
|
|
|
|
static long vorbis_q_bitrate[] = { 60, 74, 86, 106, 120, 152,
|
|
183, 207, 239, 309, 440 };
|
|
long quality = static_cast<long>(d->vorbis_quality);
|
|
if (quality < 0 || quality > 10)
|
|
quality = 3;
|
|
vorbis_size = (time_secs * vorbis_q_bitrate[quality] * 1000) / 8;
|
|
|
|
break;
|
|
}
|
|
|
|
#endif // HAVE_VORBIS >= 2
|
|
|
|
default: // bitrate based encoding
|
|
vorbis_size = (time_secs * d->vorbis_bitrate/8);
|
|
break;
|
|
}
|
|
|
|
return vorbis_size;
|
|
}
|
|
|
|
const char * EncoderVorbis::mimeType() const{
|
|
return "audio/vorbis";
|
|
}
|
|
|
|
long EncoderVorbis::readInit(long /*size*/){
|
|
ogg_packet header;
|
|
ogg_packet header_comm;
|
|
ogg_packet header_code;
|
|
|
|
vorbis_analysis_init(&d->vd,&d->vi);
|
|
vorbis_block_init(&d->vd,&d->vb);
|
|
|
|
srand(time(NULL));
|
|
ogg_stream_init(&d->os,rand());
|
|
|
|
vorbis_analysis_headerout(&d->vd,&d->vc,&header,&header_comm,&header_code);
|
|
|
|
ogg_stream_packetin(&d->os,&header);
|
|
ogg_stream_packetin(&d->os,&header_comm);
|
|
ogg_stream_packetin(&d->os,&header_code);
|
|
|
|
while (int result = ogg_stream_flush(&d->os,&d->og)) {
|
|
|
|
if (!result) break;
|
|
|
|
TQByteArray output;
|
|
|
|
char * oggheader = reinterpret_cast<char *>(d->og.header);
|
|
char * oggbody = reinterpret_cast<char *>(d->og.body);
|
|
|
|
if (d->og.header_len) {
|
|
output.setRawData(oggheader, d->og.header_len);
|
|
ioslave->data(output);
|
|
output.resetRawData(oggheader, d->og.header_len);
|
|
}
|
|
|
|
if (d->og.body_len) {
|
|
output.setRawData(oggbody, d->og.body_len);
|
|
ioslave->data(output);
|
|
output.resetRawData(oggbody, d->og.body_len);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
long EncoderVorbis::read(int16_t * buf, int frames){
|
|
int i;
|
|
float **buffer=vorbis_analysis_buffer(&d->vd,frames);
|
|
|
|
/* uninterleave samples */
|
|
for(i=0;i<frames;i++){
|
|
buffer[0][i]=buf[2*i]/32768.0;
|
|
buffer[1][i]=buf[2*i+1]/32768.0;
|
|
}
|
|
|
|
/* process chunk of data */
|
|
vorbis_analysis_wrote(&d->vd,i);
|
|
return flush_vorbis();
|
|
}
|
|
|
|
long EncoderVorbis::readCleanup(){
|
|
// send end-of-stream and flush the encoder
|
|
vorbis_analysis_wrote(&d->vd,0);
|
|
long processed = flush_vorbis();
|
|
ogg_stream_clear(&d->os);
|
|
vorbis_block_clear(&d->vb);
|
|
vorbis_dsp_clear(&d->vd);
|
|
vorbis_info_clear(&d->vi);
|
|
return processed;
|
|
}
|
|
|
|
void EncoderVorbis::fillSongInfo( KCDDB::CDInfo info, int track, const TQString &comment )
|
|
{
|
|
if( !d->write_vorbis_comments )
|
|
return;
|
|
|
|
typedef TQPair<TQCString, TQVariant> CommentField;
|
|
TQValueList<CommentField> commentFields;
|
|
|
|
commentFields.append(CommentField("title", info.trackInfoList[track].get("title")));
|
|
commentFields.append(CommentField("artist", info.get("artist")));
|
|
commentFields.append(CommentField("album", info.get("title")));
|
|
commentFields.append(CommentField("genre", info.get("genre")));
|
|
commentFields.append(CommentField("tracknumber", TQString::number(track+1)));
|
|
commentFields.append(CommentField("comment", comment));
|
|
|
|
if (info.get("year").toInt() > 0) {
|
|
TQDateTime dt(TQDate(info.get("year").toInt(), 1, 1));
|
|
commentFields.append(CommentField("date", dt.toString(TQt::ISODate).utf8().data()));
|
|
}
|
|
|
|
for(TQValueListIterator<CommentField> it = commentFields.begin(); it != commentFields.end(); ++it) {
|
|
|
|
// if the value is not empty
|
|
if(!(*it).second.toString().isEmpty()) {
|
|
|
|
char *key = tqstrdup((*it).first);
|
|
char *value = tqstrdup((*it).second.toString().utf8().data());
|
|
|
|
vorbis_comment_add_tag(&d->vc, key, value);
|
|
|
|
delete [] key;
|
|
delete [] value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // HAVE_VORBIS
|
|
|