diff options
Diffstat (limited to 'plugins')
103 files changed, 10733 insertions, 0 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am new file mode 100644 index 0000000..9523e70 --- /dev/null +++ b/plugins/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = decoder encoder audiooutput project diff --git a/plugins/audiooutput/Makefile.am b/plugins/audiooutput/Makefile.am new file mode 100644 index 0000000..d8335ad --- /dev/null +++ b/plugins/audiooutput/Makefile.am @@ -0,0 +1,9 @@ +if include_arts +ARTSDIR=arts +endif + +if include_ALSA +ALSADIR=alsa +endif + +SUBDIRS = $(ARTSDIR) $(ALSADIR) diff --git a/plugins/audiooutput/alsa/Makefile.am b/plugins/audiooutput/alsa/Makefile.am new file mode 100644 index 0000000..5cc7f06 --- /dev/null +++ b/plugins/audiooutput/alsa/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin $(all_includes) + +kde_module_LTLIBRARIES = libk3balsaoutputplugin.la + +libk3balsaoutputplugin_la_SOURCES = k3balsaoutputplugin.cpp + +libk3balsaoutputplugin_la_CFLAGS = $(ALSA_CFLAGS) +libk3balsaoutputplugin_la_LIBADD = ../../../libk3b/libk3b.la $(ALSA_LIBS) +libk3balsaoutputplugin_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3balsaoutputplugin.plugin + +METASOURCES = AUTO diff --git a/plugins/audiooutput/alsa/configure.in.bot b/plugins/audiooutput/alsa/configure.in.bot new file mode 100644 index 0000000..d5c8d17 --- /dev/null +++ b/plugins/audiooutput/alsa/configure.in.bot @@ -0,0 +1,7 @@ +echo "" + +if test "x$have_alsa" = xyes; then + echo "K3b - Audioplayer available (alsa) yes" +else + echo "K3b - Audioplayer available (alsa) no" +fi diff --git a/plugins/audiooutput/alsa/configure.in.in b/plugins/audiooutput/alsa/configure.in.in new file mode 100644 index 0000000..244dce4 --- /dev/null +++ b/plugins/audiooutput/alsa/configure.in.in @@ -0,0 +1,26 @@ +dnl --------- ALSA CHECK BEGIN ------------- + +AC_DEFUN([KDE_CHECK_ALSA], +[ + PKG_CHECK_MODULES([ALSA], [alsa >= 0.9], [have_alsa=yes], [have_alsa=no]) + AC_SUBST([ALSA_CFLAGS]) + AC_SUBST([ALSA_LIBS]) +]) + +AC_ARG_WITH(alsa, + [AS_HELP_STRING(--with-alsa, + [enable support for ALSA output @<:@default=check@:>@])], + [], with_alsa=check) + +have_alsa=no +if test "x$with_alsa" != xno; then + KDE_CHECK_ALSA + + if test "x$with_alsa" != xcheck && test "x$have_alsa" != xyes; then + AC_MSG_FAILURE([--with-alsa was given, but test for ALSA failed]) + fi +fi + +AM_CONDITIONAL(include_ALSA, [test "x$have_alsa" = "xyes"]) + +dnl --------- ALSA CHECK END --------------- diff --git a/plugins/audiooutput/alsa/k3balsaoutputplugin.cpp b/plugins/audiooutput/alsa/k3balsaoutputplugin.cpp new file mode 100644 index 0000000..7351ffc --- /dev/null +++ b/plugins/audiooutput/alsa/k3balsaoutputplugin.cpp @@ -0,0 +1,293 @@ +/* + * + * $Id: k3bartsoutputplugin.cpp 369057 2004-12-07 14:05:11Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3balsaoutputplugin.h" +#include <k3bpluginfactory.h> +#include <k3bcore.h> + +#include <kdebug.h> +#include <kcombobox.h> +#include <klocale.h> +#include <kconfig.h> +#include <kdialog.h> + +#include <qlayout.h> +#include <qlabel.h> + +#include <alsa/asoundlib.h> +#include <alsa/pcm.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3balsaoutputplugin, K3bPluginFactory<K3bAlsaOutputPlugin>( "k3balsaoutputplugin" ) ) + + +class K3bAlsaOutputPlugin::Private +{ +public: + Private() + : pcm_playback(0), + error(false) { + } + + snd_pcm_t *pcm_playback; + + bool error; + QString lastErrorMessage; + + bool swap; + + unsigned int sampleRate; +}; + + +K3bAlsaOutputPlugin::K3bAlsaOutputPlugin( QObject* parent, const char* name ) + : K3bAudioOutputPlugin( parent, name ) +{ + d = new Private; +} + + +K3bAlsaOutputPlugin::~K3bAlsaOutputPlugin() +{ + cleanup(); + + delete d; +} + + +int K3bAlsaOutputPlugin::write( char* data, int len ) +{ + if( d->error ) + return -1; + + char* buffer = data; + if( d->swap ) { + buffer = new char[len]; + for( int i = 0; i < len-1; i+=2 ) { + buffer[i] = data[i+1]; + buffer[i+1] = data[i]; + } + } + + int written = 0; + while( written < len ) { + snd_pcm_sframes_t frames = snd_pcm_writei( d->pcm_playback, + buffer+written, + snd_pcm_bytes_to_frames( d->pcm_playback, len-written ) ); + + if( frames < 0 ) { + if( !recoverFromError( frames ) ) { + d->error = true; + return -1; + } + } + else { + written += snd_pcm_frames_to_bytes( d->pcm_playback, frames ); + } + } + + return written; +} + + +bool K3bAlsaOutputPlugin::recoverFromError( int err ) +{ + if( err == -EPIPE ) { + err = snd_pcm_prepare( d->pcm_playback ); + if( err < 0 ) { + d->lastErrorMessage = i18n("Internal Alsa problem: %1").arg(snd_strerror(err)); + return false; + } + } + else if( err == -ESTRPIPE ) { + while( ( err = snd_pcm_resume( d->pcm_playback ) ) == -EAGAIN ) + sleep( 1 ); + + if (err < 0) { + // unable to wake up pcm device, restart it + err = snd_pcm_prepare( d->pcm_playback ); + if( err < 0 ) { + d->lastErrorMessage = i18n("Internal Alsa problem: %1").arg(snd_strerror(err)); + return false; + } + } + + return true; + } + + return false; +} + + +void K3bAlsaOutputPlugin::cleanup() +{ + if( d->pcm_playback ) { + snd_pcm_drain( d->pcm_playback ); + snd_pcm_close( d->pcm_playback ); + } + d->pcm_playback = 0; + d->error = false; +} + + +bool K3bAlsaOutputPlugin::init() +{ + cleanup(); + + KConfigGroup c( k3bcore->config(), "Alsa Output Plugin" ); + QString alsaDevice = c.readEntry( "output device", "default" ); + + int err = snd_pcm_open( &d->pcm_playback, alsaDevice.local8Bit(), SND_PCM_STREAM_PLAYBACK, 0 ); + if( err < 0 ) { + d->lastErrorMessage = i18n("Could not open alsa audio device '%1' (%2).").arg(alsaDevice).arg(snd_strerror(err)); + d->error = true; + return false; + } + + if( !setupHwParams() ) { + d->error = true; + return false; + } + + d->error = false; + return true; +} + + +bool K3bAlsaOutputPlugin::setupHwParams() +{ + snd_pcm_hw_params_t* hw_params; + int err = 0; + + if( ( err = snd_pcm_hw_params_malloc( &hw_params ) ) < 0 ) { + d->lastErrorMessage = i18n("Could not allocate hardware parameter structure (%1)").arg(snd_strerror(err)); + d->error = true; + return false; + } + + if( (err = snd_pcm_hw_params_any( d->pcm_playback, hw_params )) < 0) { + d->lastErrorMessage = i18n("Could not initialize hardware parameter structure (%1).").arg(snd_strerror(err)); + snd_pcm_hw_params_free( hw_params ); + d->error = true; + return false; + } + + if( (err = snd_pcm_hw_params_set_access( d->pcm_playback, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + d->lastErrorMessage = i18n("Could not set access type (%1).").arg(snd_strerror(err)); + snd_pcm_hw_params_free( hw_params ); + d->error = true; + return false; + } + + if( (err = snd_pcm_hw_params_set_format( d->pcm_playback, hw_params, SND_PCM_FORMAT_S16_BE)) < 0) { + if( (err = snd_pcm_hw_params_set_format( d->pcm_playback, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + d->lastErrorMessage = i18n("Could not set sample format (%1).").arg(snd_strerror(err)); + snd_pcm_hw_params_free( hw_params ); + d->error = true; + return false; + } + else + d->swap = true; + } + else + d->swap = false; + + d->sampleRate = 44100; + if( (err = snd_pcm_hw_params_set_rate_near( d->pcm_playback, hw_params, &d->sampleRate, 0)) < 0) { + d->lastErrorMessage = i18n("Could not set sample rate (%1).").arg(snd_strerror(err)); + snd_pcm_hw_params_free( hw_params ); + d->error = true; + return false; + } + + kdDebug() << "(K3bAlsaOutputPlugin) samplerate set to " << d->sampleRate << endl; + + if( (err = snd_pcm_hw_params_set_channels( d->pcm_playback, hw_params, 2)) < 0) { + d->lastErrorMessage = i18n("Could not set channel count (%1).").arg(snd_strerror(err)); + snd_pcm_hw_params_free( hw_params ); + d->error = true; + return false; + } + + if( (err = snd_pcm_hw_params( d->pcm_playback, hw_params )) < 0) { + d->lastErrorMessage = i18n("Could not set parameters (%1).").arg(snd_strerror(err)); + snd_pcm_hw_params_free( hw_params ); + d->error = true; + return false; + } + + snd_pcm_hw_params_free(hw_params); + + return true; +} + + +QString K3bAlsaOutputPlugin::lastErrorMessage() const +{ + return d->lastErrorMessage; +} + + +K3bPluginConfigWidget* K3bAlsaOutputPlugin::createConfigWidget( QWidget* parent, + const char* name ) const +{ + return new K3bAlsaOutputPluginConfigWidget( parent, name ); +} + + + +K3bAlsaOutputPluginConfigWidget::K3bAlsaOutputPluginConfigWidget( QWidget* parent, const char* name ) + : K3bPluginConfigWidget( parent, name ) +{ + QHBoxLayout* l = new QHBoxLayout( this ); + l->setSpacing( KDialog::spacingHint() ); + l->setAutoAdd( true ); + + (void)new QLabel( i18n("Alsa device:"), this ); + + m_comboDevice = new KComboBox( this ); + m_comboDevice->setEditable( true ); + // enable completion + m_comboDevice->completionObject(); + + // FIXME: initialize the list of devices + m_comboDevice->insertItem( "default" ); +} + + +K3bAlsaOutputPluginConfigWidget::~K3bAlsaOutputPluginConfigWidget() +{ +} + + +void K3bAlsaOutputPluginConfigWidget::loadConfig() +{ + KConfigGroup c( k3bcore->config(), "Alsa Output Plugin" ); + + m_comboDevice->setCurrentText( c.readEntry( "output device", "default" ) ); +} + + +void K3bAlsaOutputPluginConfigWidget::saveConfig() +{ + KConfigGroup c( k3bcore->config(), "Alsa Output Plugin" ); + + c.writeEntry( "output device", m_comboDevice->currentText() ); +} + + +#include "k3balsaoutputplugin.moc" diff --git a/plugins/audiooutput/alsa/k3balsaoutputplugin.h b/plugins/audiooutput/alsa/k3balsaoutputplugin.h new file mode 100644 index 0000000..7e59b1e --- /dev/null +++ b/plugins/audiooutput/alsa/k3balsaoutputplugin.h @@ -0,0 +1,69 @@ +/* + * + * $Id: k3bartsoutputplugin.h 369057 2004-12-07 14:05:11Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_ALSA_AUDIO_OUTPUT_H_ +#define _K3B_ALSA_AUDIO_OUTPUT_H_ + +#include <k3baudiooutputplugin.h> +#include <k3bpluginconfigwidget.h> + +class KComboBox; + + +class K3bAlsaOutputPlugin : public K3bAudioOutputPlugin +{ + public: + K3bAlsaOutputPlugin( QObject* parent = 0, const char* name = 0 ); + ~K3bAlsaOutputPlugin(); + + int pluginSystemVersion() const { return 3; } + QCString soundSystem() const { return "alsa"; } + + bool init(); + void cleanup(); + + QString lastErrorMessage() const; + + int write( char* data, int len ); + + K3bPluginConfigWidget* createConfigWidget( QWidget* parent = 0, + const char* name = 0 ) const; + + private: + bool setupHwParams(); + bool recoverFromError( int err ); + + class Private; + Private* d; +}; + + +class K3bAlsaOutputPluginConfigWidget : public K3bPluginConfigWidget +{ + Q_OBJECT + + public: + K3bAlsaOutputPluginConfigWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bAlsaOutputPluginConfigWidget(); + + public slots: + void loadConfig(); + void saveConfig(); + + private: + KComboBox* m_comboDevice; +}; + +#endif diff --git a/plugins/audiooutput/alsa/k3balsaoutputplugin.plugin b/plugins/audiooutput/alsa/k3balsaoutputplugin.plugin new file mode 100644 index 0000000..b5957d7 --- /dev/null +++ b/plugins/audiooutput/alsa/k3balsaoutputplugin.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3balsaoutputplugin +Group=AudioOutput +Name=K3b Alsa Audio Output Plugin +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=1.0 +Comment=Audio Output plugin which plays through alsa +License=GPL diff --git a/plugins/audiooutput/arts/Makefile.am b/plugins/audiooutput/arts/Makefile.am new file mode 100644 index 0000000..6d5adf5 --- /dev/null +++ b/plugins/audiooutput/arts/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin $(all_includes) + +kde_module_LTLIBRARIES = libk3bartsoutputplugin.la + +libk3bartsoutputplugin_la_SOURCES = k3bartsoutputplugin.cpp + +libk3bartsoutputplugin_la_LIBADD = ../../../libk3b/libk3b.la -lartsc +libk3bartsoutputplugin_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bartsoutputplugin.plugin + +METASOURCES = AUTO diff --git a/plugins/audiooutput/arts/k3bartsoutputplugin.cpp b/plugins/audiooutput/arts/k3bartsoutputplugin.cpp new file mode 100644 index 0000000..893571a --- /dev/null +++ b/plugins/audiooutput/arts/k3bartsoutputplugin.cpp @@ -0,0 +1,90 @@ +/* + * + * $Id: k3bartsoutputplugin.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bartsoutputplugin.h" +#include <k3bpluginfactory.h> + +#include <kdebug.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3bartsoutputplugin, K3bPluginFactory<K3bArtsOutputPlugin>( "k3bartsoutputplugin" ) ) + + +K3bArtsOutputPlugin::K3bArtsOutputPlugin( QObject* parent, const char* name ) + : K3bAudioOutputPlugin( parent, name ), + m_initialized(false), + m_lastErrorCode(0) +{ +} + + +K3bArtsOutputPlugin::~K3bArtsOutputPlugin() +{ + cleanup(); +} + + +int K3bArtsOutputPlugin::write( char* data, int len ) +{ + for( int i = 0; i < len-1; i+=2 ) { + char b = data[i]; + data[i] = data[i+1]; + data[i+1] = b; + } + + m_lastErrorCode = arts_write( m_stream, data, len ); + + if( m_lastErrorCode < 0 ) + return -1; + else + return len; +} + + +void K3bArtsOutputPlugin::cleanup() +{ + if( m_initialized ) { + arts_close_stream( m_stream ); + kdDebug() << "(K3bArtsOutputPlugin::cleanup) arts_free()" << endl; + arts_free(); + kdDebug() << "(K3bArtsOutputPlugin::cleanup) arts_free() done" << endl; + m_initialized = false; + } +} + + +bool K3bArtsOutputPlugin::init() +{ + kdDebug() << "(K3bArtsOutputPlugin::init)" << endl; + if( !m_initialized ) { + kdDebug() << "(K3bArtsOutputPlugin::init) arts_init()" << endl; + m_lastErrorCode = arts_init(); + m_initialized = ( m_lastErrorCode == 0 ); + kdDebug() << "(K3bArtsOutputPlugin::init) arts_init() done: " << m_lastErrorCode << endl; + if( m_initialized ) + m_stream = arts_play_stream( 44100, 16, 2, "K3bArtsOutputPlugin" ); + } + + return m_initialized; +} + + +QString K3bArtsOutputPlugin::lastErrorMessage() const +{ + return QString::fromLocal8Bit( arts_error_text(m_lastErrorCode) ); +} + diff --git a/plugins/audiooutput/arts/k3bartsoutputplugin.h b/plugins/audiooutput/arts/k3bartsoutputplugin.h new file mode 100644 index 0000000..57a2b22 --- /dev/null +++ b/plugins/audiooutput/arts/k3bartsoutputplugin.h @@ -0,0 +1,47 @@ +/* + * + * $Id: k3bartsoutputplugin.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_ARTS_AUDIO_OUTPUT_H_ +#define _K3B_ARTS_AUDIO_OUTPUT_H_ + +#include <k3baudiooutputplugin.h> + +#include <artsc/artsc.h> + + +class K3bArtsOutputPlugin : public K3bAudioOutputPlugin +{ + public: + K3bArtsOutputPlugin( QObject* parent = 0, const char* name = 0 ); + ~K3bArtsOutputPlugin(); + + int pluginSystemVersion() const { return 3; } + QCString soundSystem() const { return "arts"; } + + bool init(); + void cleanup(); + + QString lastErrorMessage() const; + + int write( char* data, int len ); + + private: + bool m_initialized; + int m_lastErrorCode; + + arts_stream_t m_stream; +}; + +#endif diff --git a/plugins/audiooutput/arts/k3bartsoutputplugin.plugin b/plugins/audiooutput/arts/k3bartsoutputplugin.plugin new file mode 100644 index 0000000..599869f --- /dev/null +++ b/plugins/audiooutput/arts/k3bartsoutputplugin.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bartsoutputplugin +Group=AudioOutput +Name=K3b Arts Audio Output Plugin +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=1.0 +Comment=Audio Output plugin which plays through arts +License=GPL diff --git a/plugins/decoder/Makefile.am b/plugins/decoder/Makefile.am new file mode 100644 index 0000000..1b4eafa --- /dev/null +++ b/plugins/decoder/Makefile.am @@ -0,0 +1,26 @@ + +if include_OGG +DECOGGDIR = ogg +endif + +if include_MP3 +DECMP3DIR = mp3 +endif + +if include_FLAC +DECFLACDIR = flac +endif + +if include_AIFF +DECAIFFDIR = libsndfile +endif + +if include_FFMPEG +FFMPEGDIR = ffmpeg +endif + +if include_MPC +MPCDIR = musepack +endif + +SUBDIRS = wave $(DECOGGDIR) $(DECMP3DIR) $(DECFLACDIR) $(DECAIFFDIR) $(FFMPEGDIR) $(MPCDIR) diff --git a/plugins/decoder/ffmpeg/Makefile.am b/plugins/decoder/ffmpeg/Makefile.am new file mode 100644 index 0000000..b4c46e2 --- /dev/null +++ b/plugins/decoder/ffmpeg/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3bdevice $(all_includes) +KDE_CXXFLAGS = -D__STDC_CONSTANT_MACROS + +kde_module_LTLIBRARIES = libk3bffmpegdecoder.la + +libk3bffmpegdecoder_la_SOURCES = k3bffmpegdecoder.cpp k3bffmpegwrapper.cpp + +libk3bffmpegdecoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDEUI) -lavcodec -lavformat +libk3bffmpegdecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bffmpegdecoder.plugin + +METASOURCES = AUTO + diff --git a/plugins/decoder/ffmpeg/configure.in.bot b/plugins/decoder/ffmpeg/configure.in.bot new file mode 100644 index 0000000..3d24645 --- /dev/null +++ b/plugins/decoder/ffmpeg/configure.in.bot @@ -0,0 +1,25 @@ +echo "" + +echo "K3b - FFMpeg decoder plugin (decodes wma and others):" +if test x$have_ffmpeg = xyes; then + echo "K3b - yes" + if test x$enable_ffmpeg_all_codecs = xyes; then + echo "K3b - WARNING: You enabled all codecs in the ffmpeg decoder plugin." + echo "K3b - Be aware that most are not tested and track lengths" + echo "K3b - will be wrong in many cases." + fi +else + echo "K3b - no" +if test "$ac_cv_use_ffmpeg" = "yes"; then + if test "$ffmpeg_compiles" = "yes"; then + echo "K3b - You are missing the ffmpeg libraries." + echo "K3b - Make sure ffmpeg has been configured as a" + echo "K3b - shared library (which is not the default)." + else + echo "K3b - You are missing the ffmpeg headers and libraries" + echo "K3b - version 0.4.9 or higher." + fi + echo "K3b - The ffmpeg audio decoding plugin (decodes wma and" + echo "K3b - others) won't be compiled." +fi +fi diff --git a/plugins/decoder/ffmpeg/configure.in.in b/plugins/decoder/ffmpeg/configure.in.in new file mode 100644 index 0000000..84b345a --- /dev/null +++ b/plugins/decoder/ffmpeg/configure.in.in @@ -0,0 +1,68 @@ +dnl --------------- FFMPEG CHECK --------------------------------- + +AC_ARG_WITH( + ffmpeg, + AS_HELP_STRING( + [--without-ffmpeg], + [build K3b without ffmpeg audio decoder support (default=no)]), + [ac_cv_use_ffmpeg=$withval], + [ac_cv_use_ffmpeg=yes] +) + +# +# The ffmpeg decoder plugin needs ffmpeg 0.4.9 or higher +# +have_ffmpeg=no +if test "$ac_cv_use_ffmpeg" = "yes"; then + k3b_cxxflags_save="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -D__STDC_CONSTANT_MACROS" + AC_MSG_CHECKING(for ffmpeg >= 0.4.9) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_COMPILE_IFELSE( + extern "C" { + #include <libavformat/avformat.h> + #include <libavcodec/avcodec.h> + } + + int main() { + AVFormatContext* fc = 0; + AVPacket* p = 0; + av_register_all(); + return av_read_frame( fc, p ); + }, + [ffmpeg_compiles=yes], [ffmpeg_compiles=no] ) + OLD_LIBS=$LIBS + LIBS="-lavformat -lavcodec $LIBS" + AC_LINK_IFELSE( + extern "C" { + #include <libavformat/avformat.h> + #include <libavcodec/avcodec.h> + } + + int main() { + AVFormatContext* fc = 0; + AVPacket* p = 0; + av_register_all(); + return av_read_frame( fc, p ); + }, + [ffmpeg_links=yes], [ffmpeg_links=no] ) + AC_LANG_RESTORE + LIBS=$OLD_LIBS + have_ffmpeg=$ffmpeg_links + AC_MSG_RESULT($have_ffmpeg) + CXXFLAGS=$k3b_cxxflags_save +fi +AM_CONDITIONAL(include_FFMPEG, [test x$have_ffmpeg = xyes]) + +dnl --------------- FFMPEG CHECK END ------------------------------ + +AC_ARG_ENABLE( + ffmpeg-all-codecs, + AS_HELP_STRING( + [--enable-ffmpeg-all-codecs], + [Build K3b's ffmeg decoder plugin with all audio codecs enabled (default=disabled)]), + [AC_DEFINE(K3B_FFMPEG_ALL_CODECS, 1, [Defined if all ffmpeg codecs should be allowed]) + enable_ffmpeg_all_codecs=yes], + [enable_ffmpeg_all_codecs=no] +) diff --git a/plugins/decoder/ffmpeg/k3bffmpegdecoder.cpp b/plugins/decoder/ffmpeg/k3bffmpegdecoder.cpp new file mode 100644 index 0000000..fd47c52 --- /dev/null +++ b/plugins/decoder/ffmpeg/k3bffmpegdecoder.cpp @@ -0,0 +1,155 @@ +/* + * + * $Id: k3bffmpegdecoder.cpp 641798 2007-03-12 16:07:10Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bffmpegdecoder.h" +#include "k3bffmpegwrapper.h" + +#include <kdebug.h> +#include <k3bpluginfactory.h> + +extern "C" { +#include <libavcodec/avcodec.h> +} + +#include <math.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3bffmpegdecoder, K3bPluginFactory<K3bFFMpegDecoderFactory>( "k3bffmpegdecoder" ) ) + + +K3bFFMpegDecoderFactory::K3bFFMpegDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bFFMpegDecoderFactory::~K3bFFMpegDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bFFMpegDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bFFMpegDecoder( parent, name ); +} + + +bool K3bFFMpegDecoderFactory::canDecode( const KURL& url ) +{ + K3bFFMpegFile* file = K3bFFMpegWrapper::instance()->open( url.path() ); + if( file ) { + delete file; + return true; + } + else { + return false; + } +} + + + + + + +K3bFFMpegDecoder::K3bFFMpegDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ), + m_file(0) +{ +} + + +K3bFFMpegDecoder::~K3bFFMpegDecoder() +{ +} + + +QString K3bFFMpegDecoder::fileType() const +{ + return m_type; +} + + +bool K3bFFMpegDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + m_file = K3bFFMpegWrapper::instance()->open( filename() ); + if( m_file ) { + + // TODO: call addTechnicalInfo + + addMetaInfo( META_TITLE, m_file->title() ); + addMetaInfo( META_ARTIST, m_file->author() ); + addMetaInfo( META_COMMENT, m_file->comment() ); + + samplerate = m_file->sampleRate(); + ch = m_file->channels(); + m_type = m_file->typeComment(); + frames = m_file->length(); + + // ffmpeg's length information is not reliable at all + // so we have to decode the whole file in order to get the correct length +// char buffer[10*2048]; +// int len = 0; +// unsigned long long bytes = 0; +// while( ( len = m_file->read( buffer, 10*2048 ) ) > 0 ) +// bytes += len; + +// frames = (unsigned long)ceil((double)bytes/2048.0); + + // cleanup; + delete m_file; + m_file = 0; + + return true; + } + else + return false; +} + + +bool K3bFFMpegDecoder::initDecoderInternal() +{ + if( !m_file ) + m_file = K3bFFMpegWrapper::instance()->open( filename() ); + + return (m_file != 0); +} + + +void K3bFFMpegDecoder::cleanup() +{ + delete m_file; + m_file = 0; +} + + +bool K3bFFMpegDecoder::seekInternal( const K3b::Msf& msf ) +{ + if( msf == 0 ) + return initDecoderInternal(); + else + return m_file->seek( msf ); +} + + +int K3bFFMpegDecoder::decodeInternal( char* _data, int maxLen ) +{ + return m_file->read( _data, maxLen ); +} + + +#include "k3bffmpegdecoder.moc" diff --git a/plugins/decoder/ffmpeg/k3bffmpegdecoder.h b/plugins/decoder/ffmpeg/k3bffmpegdecoder.h new file mode 100644 index 0000000..1c21827 --- /dev/null +++ b/plugins/decoder/ffmpeg/k3bffmpegdecoder.h @@ -0,0 +1,67 @@ +/* + * + * $Id: k3bffmpegdecoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_FFMPEG_DECODER_H_ +#define _K3B_FFMPEG_DECODER_H_ + +#include <k3baudiodecoder.h> + +class K3bFFMpegFile; + + +class K3bFFMpegDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bFFMpegDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bFFMpegDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + bool multiFormatDecoder() const { return true; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class K3bFFMpegDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bFFMpegDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bFFMpegDecoder(); + + QString fileType() const; + + void cleanup(); + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + bool seekInternal( const K3b::Msf& ); + + int decodeInternal( char* _data, int maxLen ); + + private: + K3bFFMpegFile* m_file; + QString m_type; +}; + +#endif diff --git a/plugins/decoder/ffmpeg/k3bffmpegdecoder.plugin b/plugins/decoder/ffmpeg/k3bffmpegdecoder.plugin new file mode 100644 index 0000000..3592388 --- /dev/null +++ b/plugins/decoder/ffmpeg/k3bffmpegdecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bffmpegdecoder +Group=AudioDecoder +Name=K3b FFMpeg Decoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=0.9.1 +Comment=Decoding module to decode wma files +License=GPL diff --git a/plugins/decoder/ffmpeg/k3bffmpegwrapper.cpp b/plugins/decoder/ffmpeg/k3bffmpegwrapper.cpp new file mode 100644 index 0000000..514fd67 --- /dev/null +++ b/plugins/decoder/ffmpeg/k3bffmpegwrapper.cpp @@ -0,0 +1,375 @@ +/* + * + * $Id: k3bffmpegwrapper.cpp 641819 2007-03-12 17:29:23Z trueg $ + * Copyright (C) 2004-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bffmpegwrapper.h" + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +} + +#include <string.h> + +#include <klocale.h> + + +#if LIBAVFORMAT_BUILD < 4629 +#define FFMPEG_BUILD_PRE_4629 +#endif + + +K3bFFMpegWrapper* K3bFFMpegWrapper::s_instance = 0; + + +class K3bFFMpegFile::Private +{ +public: + AVFormatContext* formatContext; + AVCodec* codec; + + K3b::Msf length; + + // for decoding + char outputBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE]; + char* outputBufferPos; + int outputBufferSize; + AVPacket packet; + Q_UINT8* packetData; + int packetSize; +}; + + +K3bFFMpegFile::K3bFFMpegFile( const QString& filename ) + : m_filename(filename) +{ + d = new Private; + d->formatContext = 0; + d->codec = 0; +} + + +K3bFFMpegFile::~K3bFFMpegFile() +{ + close(); + delete d; +} + + +bool K3bFFMpegFile::open() +{ + close(); + + // open the file + int err = av_open_input_file( &d->formatContext, m_filename.local8Bit(), 0, 0, 0 ); + if( err < 0 ) { + kdDebug() << "(K3bFFMpegFile) unable to open " << m_filename << " with error " << err << endl; + return false; + } + + // analyze the streams + av_find_stream_info( d->formatContext ); + + // we only handle files containing one audio stream + if( d->formatContext->nb_streams != 1 ) { + kdDebug() << "(K3bFFMpegFile) more than one stream in " << m_filename << endl; + return false; + } + + // urgh... ugly +#ifdef FFMPEG_BUILD_PRE_4629 + AVCodecContext* codecContext = &d->formatContext->streams[0]->codec; +#else + AVCodecContext* codecContext = d->formatContext->streams[0]->codec; +#endif + if( codecContext->codec_type != CODEC_TYPE_AUDIO ) { + kdDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename << endl; + return false; + } + + // get the codec + d->codec = avcodec_find_decoder(codecContext->codec_id); + if( !d->codec ) { + kdDebug() << "(K3bFFMpegFile) no codec found for " << m_filename << endl; + return false; + } + + // open the codec on our context + kdDebug() << "(K3bFFMpegFile) found codec for " << m_filename << endl; + if( avcodec_open( codecContext, d->codec ) < 0 ) { + kdDebug() << "(K3bFFMpegDecoderFactory) could not open codec." << endl; + return false; + } + + // determine the length of the stream + d->length = K3b::Msf::fromSeconds( (double)d->formatContext->duration / (double)AV_TIME_BASE ); + + if( d->length == 0 ) { + kdDebug() << "(K3bFFMpegDecoderFactory) invalid length." << endl; + return false; + } + + // dump some debugging info + dump_format( d->formatContext, 0, m_filename.local8Bit(), 0 ); + + return true; +} + + +void K3bFFMpegFile::close() +{ + d->outputBufferSize = 0; + d->packetSize = 0; + d->packetData = 0; + + if( d->codec ) { +#ifdef FFMPEG_BUILD_PRE_4629 + avcodec_close( &d->formatContext->streams[0]->codec ); +#else + avcodec_close( d->formatContext->streams[0]->codec ); +#endif + d->codec = 0; + } + + if( d->formatContext ) { + av_close_input_file( d->formatContext ); + d->formatContext = 0; + } +} + + +K3b::Msf K3bFFMpegFile::length() const +{ + return d->length; +} + + +int K3bFFMpegFile::sampleRate() const +{ +#ifdef FFMPEG_BUILD_PRE_4629 + return d->formatContext->streams[0]->codec.sample_rate; +#else + return d->formatContext->streams[0]->codec->sample_rate; +#endif +} + + +int K3bFFMpegFile::channels() const +{ +#ifdef FFMPEG_BUILD_PRE_4629 + return d->formatContext->streams[0]->codec.channels; +#else + return d->formatContext->streams[0]->codec->channels; +#endif +} + + +int K3bFFMpegFile::type() const +{ +#ifdef FFMPEG_BUILD_PRE_4629 + return d->formatContext->streams[0]->codec.codec_id; +#else + return d->formatContext->streams[0]->codec->codec_id; +#endif +} + + +QString K3bFFMpegFile::typeComment() const +{ + switch( type() ) { + case CODEC_ID_WMAV1: + return i18n("Windows Media v1"); + case CODEC_ID_WMAV2: + return i18n("Windows Media v2"); + case CODEC_ID_MP3: + return i18n("MPEG 1 Layer III"); + case CODEC_ID_AAC: + return i18n("Advanced Audio Coding (AAC)"); + default: + return QString::fromLocal8Bit( d->codec->name ); + } +} + + +QString K3bFFMpegFile::title() const +{ + // FIXME: is this UTF8 or something?? + if( d->formatContext->title[0] != '\0' ) + return QString::fromLocal8Bit( d->formatContext->title ); + else + return QString::null; +} + + +QString K3bFFMpegFile::author() const +{ + // FIXME: is this UTF8 or something?? + if( d->formatContext->author[0] != '\0' ) + return QString::fromLocal8Bit( d->formatContext->author ); + else + return QString::null; +} + + +QString K3bFFMpegFile::comment() const +{ + // FIXME: is this UTF8 or something?? + if( d->formatContext->comment[0] != '\0' ) + return QString::fromLocal8Bit( d->formatContext->comment ); + else + return QString::null; +} + + +int K3bFFMpegFile::read( char* buf, int bufLen ) +{ + if( fillOutputBuffer() > 0 ) { + int len = QMIN(bufLen, d->outputBufferSize); + ::memcpy( buf, d->outputBufferPos, len ); + + // TODO: only swap if needed + for( int i = 0; i < len-1; i+=2 ) { + char a = buf[i]; + buf[i] = buf[i+1]; + buf[i+1] = a; + } + + d->outputBufferPos += len; + d->outputBufferSize -= len; + return len; + } + else + return 0; +} + + +// fill d->packetData with data to decode +int K3bFFMpegFile::readPacket() +{ + if( d->packetSize <= 0 ) { + av_init_packet( &d->packet ); + + if( av_read_frame( d->formatContext, &d->packet ) < 0 ) { + return 0; + } + + d->packetSize = d->packet.size; + d->packetData = d->packet.data; + } + + return d->packetSize; +} + + +// decode data in d->packetData and fill d->outputBuffer +int K3bFFMpegFile::fillOutputBuffer() +{ + // decode if the output buffer is empty + if( d->outputBufferSize <= 0 ) { + + // make sure we have data to decode + if( readPacket() == 0 ) { + return 0; + } + + d->outputBufferPos = d->outputBuffer; + +#ifdef FFMPEG_BUILD_PRE_4629 + int len = avcodec_decode_audio2( &d->formatContext->streams[0]->codec, +#else + int len = avcodec_decode_audio2( d->formatContext->streams[0]->codec, +#endif + (short*)d->outputBuffer, &d->outputBufferSize, + d->packetData, d->packetSize ); + + d->packetSize -= len; + d->packetData += len; + + if( d->packetSize <= 0 ) + av_free_packet( &d->packet ); + } + + // if it is still empty try again + if( d->outputBufferSize <= 0 ) + return fillOutputBuffer(); + else + return d->outputBufferSize; +} + + +bool K3bFFMpegFile::seek( const K3b::Msf& msf ) +{ + d->outputBufferSize = 0; + d->packetSize = 0; + + double seconds = (double)msf.totalFrames()/75.0; + Q_UINT64 timestamp = (Q_UINT64)(seconds * (double)AV_TIME_BASE); + + // FIXME: do we really need the start_time and why? +#if LIBAVFORMAT_BUILD >= 4619 + return ( av_seek_frame( d->formatContext, -1, timestamp + d->formatContext->start_time, 0 ) >= 0 ); +#else + return ( av_seek_frame( d->formatContext, -1, timestamp + d->formatContext->start_time ) >= 0 ); +#endif +} + + + + + + +K3bFFMpegWrapper::K3bFFMpegWrapper() +{ + av_register_all(); +} + + +K3bFFMpegWrapper::~K3bFFMpegWrapper() +{ + s_instance = 0; +} + + +K3bFFMpegWrapper* K3bFFMpegWrapper::instance() +{ + if( !s_instance ) { + s_instance = new K3bFFMpegWrapper(); + } + + return s_instance; +} + + +K3bFFMpegFile* K3bFFMpegWrapper::open( const QString& filename ) const +{ + K3bFFMpegFile* file = new K3bFFMpegFile( filename ); + if( file->open() ) { +#ifndef K3B_FFMPEG_ALL_CODECS + // + // only allow tested formats. ffmpeg seems not to be too reliable with every format. + // mp3 being one of them sadly. Most importantly: allow the libsndfile decoder to do + // its thing. + // + if( file->type() == CODEC_ID_WMAV1 || + file->type() == CODEC_ID_WMAV2 || + file->type() == CODEC_ID_AAC ) +#endif + return file; + } + + delete file; + return 0; +} diff --git a/plugins/decoder/ffmpeg/k3bffmpegwrapper.h b/plugins/decoder/ffmpeg/k3bffmpegwrapper.h new file mode 100644 index 0000000..63b5f58 --- /dev/null +++ b/plugins/decoder/ffmpeg/k3bffmpegwrapper.h @@ -0,0 +1,85 @@ +/* + * + * $Id: k3bffmpegwrapper.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_FFMPEG_WRAPPER_H_ +#define _K3B_FFMPEG_WRAPPER_H_ + +#include <k3bmsf.h> + + + +/** + * Create with K3bFFMpegWrapper::open + */ +class K3bFFMpegFile +{ + friend class K3bFFMpegWrapper; + + public: + ~K3bFFMpegFile(); + + const QString& filename() const { return m_filename; } + + bool open(); + void close(); + + K3b::Msf length() const; + int sampleRate() const; + int channels() const; + + /** + * ffmpeg internal enumeration + */ + int type() const; + QString typeComment() const; + + QString title() const; + QString author() const; + QString comment() const; + + int read( char* buf, int bufLen ); + bool seek( const K3b::Msf& ); + + private: + K3bFFMpegFile( const QString& filename ); + int readPacket(); + int fillOutputBuffer(); + + QString m_filename; + + class Private; + Private* d; +}; + + +class K3bFFMpegWrapper +{ + public: + ~K3bFFMpegWrapper(); + + /** + * returns 0 on failure. + */ + K3bFFMpegFile* open( const QString& filename ) const; + + static K3bFFMpegWrapper* instance(); + + private: + K3bFFMpegWrapper(); + + static K3bFFMpegWrapper* s_instance; +}; + +#endif diff --git a/plugins/decoder/flac/Makefile.am b/plugins/decoder/flac/Makefile.am new file mode 100644 index 0000000..bdcc4a3 --- /dev/null +++ b/plugins/decoder/flac/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3bdevice -I$(srcdir)/../../../libk3b/core $(taglib_includes) $(all_includes) + +kde_module_LTLIBRARIES = libk3bflacdecoder.la + +libk3bflacdecoder_la_SOURCES = k3bflacdecoder.cpp + +libk3bflacdecoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDEUI) -lFLAC++ -lFLAC $(taglib_libs) +libk3bflacdecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bflacdecoder.plugin + +METASOURCES = AUTO diff --git a/plugins/decoder/flac/configure.in.bot b/plugins/decoder/flac/configure.in.bot new file mode 100644 index 0000000..f2d95a4 --- /dev/null +++ b/plugins/decoder/flac/configure.in.bot @@ -0,0 +1,13 @@ +echo "" + +if test x$have_flac = xyes; then + echo "K3b - FLAC support: yes" +else + echo "K3b - FLAC support: no" +if test "$ac_cv_use_flac" = "yes"; then + if test "$have_flac" = "no"; then + echo "K3b - You are missing the FLAC++ headers and libraries." + echo "K3b - The FLAC decoding plugin won't be compiled." + fi +fi +fi diff --git a/plugins/decoder/flac/configure.in.in b/plugins/decoder/flac/configure.in.in new file mode 100644 index 0000000..e6a3700 --- /dev/null +++ b/plugins/decoder/flac/configure.in.in @@ -0,0 +1,41 @@ +dnl === test for FLAC++ and FLAC - begin ==== +AC_ARG_WITH( + flac, + AS_HELP_STRING([--without-flac], [build K3b without FLAC support (default=no)]), + [ac_cv_use_flac=$withval], + [ac_cv_use_flac=yes] +) + +have_flac=no +if test "$ac_cv_use_flac" = "yes"; then + KDE_CHECK_HEADERS(FLAC++/decoder.h, [ + AC_CHECK_LIB(FLAC,FLAC__stream_decoder_process_single, + have_flac=yes,[],$all_libraries)]) + + AC_MSG_CHECKING(for libFLAC newer than 1.1.1) + AC_CACHE_VAL(k3b_flac_new, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + AC_TRY_COMPILE( + [ + #include <FLAC++/metadata.h> + ], [ + FLAC::Metadata::VorbisComment* vc; + vc->get_vendor_string().get_field(); + ], k3b_flac_new=no, k3b_flac_new=yes ) + AC_LANG_RESTORE + ]) + AC_MSG_RESULT($k3b_flac_new) + if test $k3b_flac_new = yes; then + AC_DEFINE(FLAC_NEWER_THAN_1_1_1, + 1, + [Define to 1 if your flac library's version is newer than or equal to 1.1.2] + ) + fi +else + have_flac=no +fi + +AM_CONDITIONAL(include_FLAC, [test x$have_flac = xyes]) +dnl === test for FLAC++ and FLAC - end ==== diff --git a/plugins/decoder/flac/k3bflacdecoder.cpp b/plugins/decoder/flac/k3bflacdecoder.cpp new file mode 100644 index 0000000..762403f --- /dev/null +++ b/plugins/decoder/flac/k3bflacdecoder.cpp @@ -0,0 +1,494 @@ +/* + * FLAC decoder module for K3b. + * Based on the Ogg Vorbis module for same. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * Copyright (C) 2003-2004 John Steele Scott <toojays@toojays.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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bflacdecoder.h" + +#include <k3bpluginfactory.h> + +#include <qbuffer.h> +#include <qfile.h> +#include <qstringlist.h> + +#include <kurl.h> +#include <kdebug.h> +#include <klocale.h> + +#include <string.h> +#include <math.h> +#include <FLAC++/metadata.h> +#include <FLAC++/decoder.h> + +#ifdef HAVE_TAGLIB +#include <taglib/tag.h> +#include <taglib/flacfile.h> +#endif + +#if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6 +#define LEGACY_FLAC +#else +#undef LEGACY_FLAC +#endif + +K_EXPORT_COMPONENT_FACTORY( libk3bflacdecoder, K3bPluginFactory<K3bFLACDecoderFactory>( "libk3bflacdecoder" ) ) + + +class K3bFLACDecoder::Private +#ifdef LEGACY_FLAC + : public FLAC::Decoder::SeekableStream +#else + : public FLAC::Decoder::Stream +#endif +{ +public: + void open(QFile* f) { + file = f; + file->open(IO_ReadOnly); + + internalBuffer->flush(); + + set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO); + set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT); + + init(); + process_until_end_of_metadata(); + } + + void cleanup() { + file->close(); + finish(); + delete comments; + comments = 0; + } + + Private(QFile* f) +#ifdef LEGACY_FLAC + : FLAC::Decoder::SeekableStream(), +#else + : FLAC::Decoder::Stream(), +#endif + comments(0) { + internalBuffer = new QBuffer(); + internalBuffer->open(IO_ReadWrite); + + open(f); + } + + + ~Private() { + cleanup(); + delete internalBuffer; + } + + bool seekToFrame(int frame); + + QFile* file; + QBuffer* internalBuffer; + FLAC::Metadata::VorbisComment* comments; + unsigned rate; + unsigned channels; + unsigned bitsPerSample; + unsigned maxFramesize; + unsigned maxBlocksize; + unsigned minFramesize; + unsigned minBlocksize; + FLAC__uint64 samples; + +protected: +#ifdef LEGACY_FLAC + virtual FLAC__SeekableStreamDecoderReadStatus read_callback(FLAC__byte buffer[], unsigned *bytes); + virtual FLAC__SeekableStreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset); + virtual FLAC__SeekableStreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset); + virtual FLAC__SeekableStreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length); +#else + virtual FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes); + virtual FLAC__StreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset); + virtual FLAC__StreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset); + virtual FLAC__StreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length); +#endif + virtual bool eof_callback(); + virtual void error_callback(FLAC__StreamDecoderErrorStatus){}; + virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata); + virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +}; + +bool K3bFLACDecoder::Private::seekToFrame(int frame) { + FLAC__uint64 sample = frame * rate / 75; + return seek_absolute(sample); +} + +bool K3bFLACDecoder::Private::eof_callback() { + return file->atEnd(); +} + +#ifdef LEGACY_FLAC +FLAC__SeekableStreamDecoderReadStatus K3bFLACDecoder::Private::read_callback(FLAC__byte buffer[], unsigned *bytes) { + long retval = file->readBlock((char *)buffer, (*bytes)); + if(-1 == retval) { + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; + } else { + (*bytes) = retval; + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; + } +} +#else +FLAC__StreamDecoderReadStatus K3bFLACDecoder::Private::read_callback(FLAC__byte buffer[], size_t *bytes) { + long retval = file->readBlock((char *)buffer, (*bytes)); + if(-1 == retval) { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } else { + (*bytes) = retval; + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } +} +#endif + +#ifdef LEGACY_FLAC +FLAC__SeekableStreamDecoderSeekStatus +K3bFLACDecoder::Private::seek_callback(FLAC__uint64 absolute_byte_offset) { + if(!file->at(absolute_byte_offset)) + return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; +} +#else +FLAC__StreamDecoderSeekStatus +K3bFLACDecoder::Private::seek_callback(FLAC__uint64 absolute_byte_offset) { + if(file->at(absolute_byte_offset) == FALSE) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} +#endif + +#ifdef LEGACY_FLAC +FLAC__SeekableStreamDecoderTellStatus +K3bFLACDecoder::Private::tell_callback(FLAC__uint64 *absolute_byte_offset) { + (*absolute_byte_offset) = file->at(); + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; +} +#else +FLAC__StreamDecoderTellStatus +K3bFLACDecoder::Private::tell_callback(FLAC__uint64 *absolute_byte_offset) { + (*absolute_byte_offset) = file->at(); + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} +#endif + +#ifdef LEGACY_FLAC +FLAC__SeekableStreamDecoderLengthStatus +K3bFLACDecoder::Private::length_callback(FLAC__uint64 *stream_length) { + (*stream_length) = file->size(); + return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; +} +#else +FLAC__StreamDecoderLengthStatus +K3bFLACDecoder::Private::length_callback(FLAC__uint64 *stream_length) { + (*stream_length) = file->size(); + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; +} +#endif + + +void K3bFLACDecoder::Private::metadata_callback(const FLAC__StreamMetadata *metadata) { + switch (metadata->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + channels = metadata->data.stream_info.channels; + rate = metadata->data.stream_info.sample_rate; + bitsPerSample = metadata->data.stream_info.bits_per_sample; + samples = metadata->data.stream_info.total_samples; + maxFramesize = metadata->data.stream_info.max_framesize; + minFramesize = metadata->data.stream_info.min_framesize; + maxBlocksize = metadata->data.stream_info.max_blocksize; + minBlocksize = metadata->data.stream_info.min_blocksize; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + comments = new FLAC::Metadata::VorbisComment((FLAC__StreamMetadata *)metadata, true); + break; + default: + break; + } +} + +FLAC__StreamDecoderWriteStatus K3bFLACDecoder::Private::write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) { + unsigned i, j; + // Note that in canDecode we made sure that the input is 1-16 bit stereo or mono. + unsigned samples = frame->header.blocksize; + + for(i=0; i < samples; i++) { + // in FLAC channel 0 is left, 1 is right + for(j=0; j < this->channels; j++) { + FLAC__int32 value = (buffer[j][i])<<(16 - frame->header.bits_per_sample); + internalBuffer->putch(value >> 8); // msb + internalBuffer->putch(value & 0xFF); // lsb + } + } + + // Rewind the buffer so the decode method will take data from the beginning. + internalBuffer->at(0); + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +K3bFLACDecoder::K3bFLACDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ + d = 0; +} + + +K3bFLACDecoder::~K3bFLACDecoder() +{ + delete d; +} + +void K3bFLACDecoder::cleanup() +{ + if (d) { + d->cleanup(); + d->open(new QFile(filename())); + } + else + d = new Private(new QFile(filename())); +} + +bool K3bFLACDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + cleanup(); + + frames = (unsigned long)ceil((d->samples * 75.0)/d->rate); + samplerate = d->rate; + ch = d->channels; + + // add meta info + if( d->comments != 0 ) { + kdDebug() << "(K3bFLACDecoder) unpacking Vorbis tags" << endl; + for( unsigned int i = 0; i < d->comments->get_num_comments(); ++i ) { + QString key = QString::fromUtf8( d->comments->get_comment(i).get_field_name(), + d->comments->get_comment(i).get_field_name_length() ); + QString value = QString::fromUtf8( d->comments->get_comment(i).get_field_value(), + d->comments->get_comment(i).get_field_value_length() ); + + if( key.upper() == "TITLE" ) + addMetaInfo( META_TITLE, value ); + else if( key.upper() == "ARTIST" ) + addMetaInfo( META_ARTIST, value ); + else if( key.upper() == "DESCRIPTION" ) + addMetaInfo( META_COMMENT, value ); + } + } +#ifdef HAVE_TAGLIB + if ((d->comments == 0) || (d->comments->get_num_comments() == 0)) { + // no Vorbis comments, check for ID3 tags + kdDebug() << "(K3bFLACDecoder) using taglib to read tag" << endl; + TagLib::FLAC::File f( QFile::encodeName(filename()) ); + if( f.isOpen() ) { + addMetaInfo( META_TITLE, TStringToQString( f.tag()->title() ) ); + addMetaInfo( META_ARTIST, TStringToQString( f.tag()->artist() ) ); + addMetaInfo( META_COMMENT, TStringToQString( f.tag()->comment() ) ); + } + } +#endif + + return true; +} + + +bool K3bFLACDecoder::initDecoderInternal() +{ + cleanup(); + + return true; +} + + +int K3bFLACDecoder::decodeInternal( char* _data, int maxLen ) +{ + int bytesToCopy; + int bytesCopied; + int bytesAvailable; + +#ifdef LEGACY_FLAC + if(d->internalBuffer->size() == 0) { + // want more data + switch(d->get_state()) { + case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM: + d->finish(); + break; + case FLAC__SEEKABLE_STREAM_DECODER_OK: + if(! d->process_single()) + return -1; + break; + default: + return -1; + } + } +#else + if(d->internalBuffer->size() == 0) { + // want more data + if(d->get_state() == FLAC__STREAM_DECODER_END_OF_STREAM) + d->finish(); + else if(d->get_state() < FLAC__STREAM_DECODER_END_OF_STREAM) { + if(! d->process_single()) + return -1; + } + else + return -1; + } +#endif + + bytesAvailable = d->internalBuffer->size() - d->internalBuffer->at(); + bytesToCopy = QMIN(maxLen, bytesAvailable); + bytesCopied = (int)d->internalBuffer->readBlock(_data, bytesToCopy); + + if(bytesCopied == bytesAvailable) { + // reset the buffer + d->internalBuffer->close(); + d->internalBuffer->open(IO_ReadWrite|IO_Truncate); + } + + return bytesCopied; +} + + +bool K3bFLACDecoder::seekInternal( const K3b::Msf& pos ) +{ + return d->seekToFrame(pos.totalFrames()); +} + + +QString K3bFLACDecoder::fileType() const +{ + return i18n("FLAC"); +} + + +QStringList K3bFLACDecoder::supportedTechnicalInfos() const +{ + return QStringList::split( ";", + i18n("Channels") + ";" + + i18n("Sampling Rate") + ";" + + i18n("Sample Size") ); +} + + +QString K3bFLACDecoder::technicalInfo( const QString& info ) const +{ + if( d->comments != 0 ) { + if( info == i18n("Vendor") ) +#ifdef FLAC_NEWER_THAN_1_1_1 + return QString::fromUtf8((char*)d->comments->get_vendor_string()); +#else + return QString::fromUtf8(d->comments->get_vendor_string().get_field()); +#endif + else if( info == i18n("Channels") ) + return QString::number(d->channels); + else if( info == i18n("Sampling Rate") ) + return i18n("%1 Hz").arg(d->rate); + else if( info == i18n("Sample Size") ) + return i18n("%1 bits").arg(d->bitsPerSample); + } + + return QString::null; +} + + + +K3bFLACDecoderFactory::K3bFLACDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bFLACDecoderFactory::~K3bFLACDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bFLACDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bFLACDecoder( parent, name ); +} + + +bool K3bFLACDecoderFactory::canDecode( const KURL& url ) +{ + // buffer large enough to read an ID3 tag header + char buf[10]; + + // Note: since file is created on the stack it will be closed automatically + // by its destructor when this method (i.e. canDecode) returns. + QFile file(url.path()); + + if(!file.open(IO_ReadOnly)) { + kdDebug() << "(K3bFLACDecoder) Could not open file " << url.path() << endl; + return false; + } + + // look for a fLaC magic number or ID3 tag header + if(10 != file.readBlock(buf, 10)) { + kdDebug() << "(K3bFLACDecorder) File " << url.path() + << " is too small to be a FLAC file" << endl; + return false; + } + + if(0 == memcmp(buf, "ID3", 3)) { + // Found ID3 tag, try and seek past it. + kdDebug() << "(K3bFLACDecorder) File " << url.path() << ": found ID3 tag" << endl; + + // See www.id3.org for details of the header, note that the size field + // unpacks to 7-bit bytes, then the +10 is for the header itself. + int pos; + pos = ((buf[6]<<21)|(buf[7]<<14)|(buf[8]<<7)|buf[9]) + 10; + + kdDebug() << "(K3bFLACDecoder) " << url.path() << ": seeking to " + << pos << endl; + if(!file.at(pos)) { + kdDebug() << "(K3bFLACDecoder) " << url.path() << ": couldn't seek to " + << pos << endl; + return false; + }else{ + // seek was okay, try and read magic number into buf + if(4 != file.readBlock(buf, 4)) { + kdDebug() << "(K3bFLACDecorder) File " << url.path() + << " has ID3 tag but naught else!" << endl; + return false; + } + } + } + + if(memcmp(buf, "fLaC", 4) != 0) { + kdDebug() << "(K3bFLACDecoder) " << url.path() << ": not a FLAC file" << endl; + return false; + } + + FLAC::Metadata::StreamInfo info = FLAC::Metadata::StreamInfo(); + FLAC::Metadata::get_streaminfo(url.path().ascii(), info); + + if((info.get_channels() <= 2) && + (info.get_bits_per_sample() <= 16)) { + return true; + } else { + kdDebug() << "(K3bFLACDecoder) " << url.path() << ": wrong format:" << endl + << " channels: " + << QString::number(info.get_channels()) << endl + << " samplerate: " + << QString::number(info.get_sample_rate()) << endl + << " bits/sample: " + << QString::number(info.get_bits_per_sample()) << endl; + return false; + } +} + +#include "k3bflacdecoder.moc" diff --git a/plugins/decoder/flac/k3bflacdecoder.h b/plugins/decoder/flac/k3bflacdecoder.h new file mode 100644 index 0000000..aace651 --- /dev/null +++ b/plugins/decoder/flac/k3bflacdecoder.h @@ -0,0 +1,68 @@ +/* + * FLAC decoder module for K3b. + * Based on the Ogg Vorbis module for same. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * Copyright (C) 2003 John Steele Scott <toojays@toojays.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. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_FLAC_DECODER_H_ +#define _K3B_FLAC_DECODER_H_ + + +#include <k3baudiodecoder.h> + +class KURL; + + +class K3bFLACDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bFLACDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bFLACDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class K3bFLACDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bFLACDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bFLACDecoder(); + + void cleanup(); + + bool seekInternal( const K3b::Msf& ); + + QString fileType() const; + QStringList supportedTechnicalInfos() const; + QString technicalInfo( const QString& ) const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + + int decodeInternal( char* _data, int maxLen ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/plugins/decoder/flac/k3bflacdecoder.plugin b/plugins/decoder/flac/k3bflacdecoder.plugin new file mode 100644 index 0000000..1e1bcbb --- /dev/null +++ b/plugins/decoder/flac/k3bflacdecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bflacdecoder +Group=AudioDecoder +Name=K3b FLAC Decoder +Author=John Steele Scott +Email=toojays@toojays.net +Version=2.1 +Comment=Decoding module to decode FLAC files +License=GPL diff --git a/plugins/decoder/libsndfile/Makefile.am b/plugins/decoder/libsndfile/Makefile.am new file mode 100644 index 0000000..2af9911 --- /dev/null +++ b/plugins/decoder/libsndfile/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3blibsndfiledecoder.la + +libk3blibsndfiledecoder_la_SOURCES = k3blibsndfiledecoder.cpp + +libk3blibsndfiledecoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDEUI) -lsndfile +libk3blibsndfiledecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3blibsndfiledecoder.plugin + +METASOURCES = AUTO + diff --git a/plugins/decoder/libsndfile/configure.in.bot b/plugins/decoder/libsndfile/configure.in.bot new file mode 100644 index 0000000..cf1e4e9 --- /dev/null +++ b/plugins/decoder/libsndfile/configure.in.bot @@ -0,0 +1,11 @@ +echo "" + +if $av_cv_sndfile; then + echo "K3b - libsndfile audio decoding support: yes" +else + echo "K3b - libsndfile audio decoding support: no" +if test "$ac_cv_use_sndfile" = "yes"; then + echo "K3b - You are missing the libsndfile headers and libraries." + echo "K3b - The libsndfile audio decoding plugin won't be compiled." +fi +fi diff --git a/plugins/decoder/libsndfile/configure.in.in b/plugins/decoder/libsndfile/configure.in.in new file mode 100644 index 0000000..e0efcc5 --- /dev/null +++ b/plugins/decoder/libsndfile/configure.in.in @@ -0,0 +1,52 @@ +dnl === test for libsndfile - begin === +dnl +dnl Don't use PKG_CHECK, since if there is no pkg-config installed, +dnl then there is no auto* magic for it either. +dnl +dnl Tests copied from kdebase/kioslave/thumbnail/ +dnl +if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +fi + +AC_ARG_WITH( + sndfile, + AS_HELP_STRING([--without-sndfile], + [build K3b without libsndfile support (default=no)]), + [ac_cv_use_sndfile=$withval], + [ac_cv_use_sndfile=yes] +) + +if test "$ac_cv_use_sndfile" = "yes"; then + SNDFILE_CFLAGS="" + SNDFILE_LIBS="" + if test "$PKG_CONFIG" = "no" ; then + ac_cv_sndfile=0 + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + if !(`$PKG_CONFIG --exists sndfile`) ; then + echo "*** sndfile is not installed." + ac_cv_sndfile=0 + else + if !(`$PKG_CONFIG --atleast-version="1.0.2" sndfile`) ; then + echo "*** You need at least version 1.0.2 of sndfile." + ac_cv_sndfile=0 + else + ac_cv_sndfile=1 + SNDFILE_CFLAGS=`$PKG_CONFIG --cflags sndfile` + SNDFILE_LIBS=`$PKG_CONFIG --libs sndfile` + fi + fi + fi + + AC_DEFINE_UNQUOTED([HAVE_SNDFILE],${ac_cv_sndfile}, + [Set to 1 if you have libsndfile.]) + AC_SUBST(SNDFILE_CFLAGS) + AC_SUBST(SNDFILE_LIBS) +fi + +AM_CONDITIONAL(include_AIFF, [test x$ac_cv_sndfile = x1]) +dnl === test for libsndfile - end === diff --git a/plugins/decoder/libsndfile/k3blibsndfiledecoder.cpp b/plugins/decoder/libsndfile/k3blibsndfiledecoder.cpp new file mode 100644 index 0000000..ea3d014 --- /dev/null +++ b/plugins/decoder/libsndfile/k3blibsndfiledecoder.cpp @@ -0,0 +1,254 @@ +/* + * + * $Id: k3blibsndfiledecoder.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Matthieu Bedouet <mbedouet@no-log.org> + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3blibsndfiledecoder.h" + +#include <k3bpluginfactory.h> + +#include <qfile.h> +#include <qstringlist.h> + +#include <kurl.h> +#include <kdebug.h> +#include <klocale.h> +#include <kinstance.h> + + +#include <math.h> +#include <stdio.h> +#include <sndfile.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3blibsndfiledecoder, K3bPluginFactory<K3bLibsndfileDecoderFactory>( "libk3blibsndfiledecoder" ) ) + + +class K3bLibsndfileDecoder::Private +{ +public: + Private(): + isOpen(false), + buffer(0), + bufferSize(0) { + format_info.name = 0; + } + + SNDFILE *sndfile; + SF_INFO sndinfo; + SF_FORMAT_INFO format_info; + bool isOpen; + float* buffer; + int bufferSize; +}; + + + +K3bLibsndfileDecoder::K3bLibsndfileDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ + d = new Private(); +} + + +K3bLibsndfileDecoder::~K3bLibsndfileDecoder() +{ + delete d; +} + + +QString K3bLibsndfileDecoder::fileType() const +{ + if( d->format_info.name ) + return QString::fromLocal8Bit(d->format_info.name); + else + return "-"; +} + + + +bool K3bLibsndfileDecoder::openFile() +{ + if( !d->isOpen ) { + + cleanup(); + + d->sndinfo.format = 0; + d->sndfile = sf_open (QFile::encodeName(filename()), SFM_READ, &d->sndinfo); + if ( !d->sndfile ) { + kdDebug() << "(K3bLibsndfileDecoder::openLibsndfileFile) : " << sf_strerror(d->sndfile) << endl; + return false; + } + else { + //retrieve infos (name, extension) about the format: + d->format_info.format = d->sndinfo.format & SF_FORMAT_TYPEMASK ; + sf_command (d->sndfile, SFC_GET_FORMAT_INFO, &d->format_info, sizeof (SF_FORMAT_INFO)) ; + + d->isOpen = true; + kdDebug() << "(K3bLibsndfileDecoder::openLibsndfileFile) " << d->format_info.name << " file opened " << endl; + return true; + } + } + + return d->isOpen; +} + + +bool K3bLibsndfileDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + cleanup(); + + if( openFile() ) { + // check length of track + if ( d->sndinfo.frames <= 0 ) { + kdDebug() << "(K3bLibsndfileDecoder::analyseFileInternal) Could not determine length of file " + << filename() << endl; + cleanup(); + return false; + } + else { + addMetaInfo( META_TITLE, sf_get_string(d->sndfile, SF_STR_TITLE) ); + addMetaInfo( META_ARTIST, sf_get_string(d->sndfile, SF_STR_ARTIST) ); + addMetaInfo( META_COMMENT, sf_get_string(d->sndfile, SF_STR_COMMENT) ); + + addTechnicalInfo( i18n("Channels"), QString::number(d->sndinfo.channels) ); + addTechnicalInfo( i18n("Sampling Rate"), i18n("%1 Hz").arg(d->sndinfo.samplerate) ); + + frames = (unsigned long)ceil(d->sndinfo.frames / d->sndinfo.samplerate * 75.0); + samplerate = d->sndinfo.samplerate; + ch = d->sndinfo.channels; + + kdDebug() << "(K3bLibsndfileDecoder) successfully analysed file: " << frames << " frames." << endl; + + cleanup(); + return true; + } + } + else + return false; +} + + + +bool K3bLibsndfileDecoder::initDecoderInternal() +{ + cleanup(); + return openFile(); +} + + + +int K3bLibsndfileDecoder::decodeInternal( char* data, int maxLen ) +{ + if( !d->buffer ) { + d->buffer = new float[maxLen]; + d->bufferSize = maxLen/2; + } + + int read = (int) sf_read_float(d->sndfile, d->buffer,d->bufferSize) ; + fromFloatTo16BitBeSigned( d->buffer, data, read ); + read = read * 2; + + if( read < 0 ) { + kdDebug() << "(K3bLibsndfileDecoder::decodeInternal) Error: " << read << endl; + return -1; + } + else if( read == 0 ) { + kdDebug() << "(K3bLibsndfileDecoder::decodeInternal) successfully finished decoding." << endl; + return 0; + } + else if( read != maxLen ) { + kdDebug() << "(K3bLibsndfileDecoder::decodeInternal) read:" << read << " expected:" << maxLen << endl; + return -1; + } + else + return read; + } + + + +bool K3bLibsndfileDecoder::seekInternal( const K3b::Msf& pos) +{ + return ( sf_seek( d->sndfile, pos.pcmSamples(), SEEK_SET ) == 0 ); +} + + + + +void K3bLibsndfileDecoder::cleanup() +{ + if( d->isOpen ) { + kdDebug() << "(K3bLibsndfileDecoder) cleaning up." << endl; + sf_close( d->sndfile ); + d->isOpen = false; + } +} + + + +/********************************************************/ + + +K3bLibsndfileDecoderFactory::K3bLibsndfileDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bLibsndfileDecoderFactory::~K3bLibsndfileDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bLibsndfileDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bLibsndfileDecoder( parent, name ); +} + + +bool K3bLibsndfileDecoderFactory::canDecode( const KURL& url ) +{ + SF_INFO infos; + infos.format = 0; + SNDFILE* sndfile = sf_open (QFile::encodeName(url.path()), SFM_READ, &infos); + + //is it supported by libsndfile? + if ( !sndfile ) { + kdDebug() << "(K3bLibsndfileDecoder) " << sf_strerror(sndfile) << endl; + return false; + } + //we exclude only WAVE as there is another plugin for this + else if ( infos.format && ((infos.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) ) { + + //retrieve infos (name) about the format: + SF_FORMAT_INFO format_info; + format_info.format = infos.format & SF_FORMAT_TYPEMASK ; + sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ; + + kdDebug() << "(K3bLibsndfileDecoder) " << format_info.name << " file === OK === " << endl; + sf_close( sndfile ); + return true; + } + else { + kdDebug() << "(K3bLibsndfileDecoder) " << url.path() << "not supported" << endl; + sf_close( sndfile ); + return false; + } + return false; +} + +#include "k3blibsndfiledecoder.moc" diff --git a/plugins/decoder/libsndfile/k3blibsndfiledecoder.h b/plugins/decoder/libsndfile/k3blibsndfiledecoder.h new file mode 100644 index 0000000..7ffbe2d --- /dev/null +++ b/plugins/decoder/libsndfile/k3blibsndfiledecoder.h @@ -0,0 +1,69 @@ +/* + * + * $Id: k3blibsndfiledecoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Matthieu Bedouet <mbedouet@no-log.org> + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_AIFF_DECODER_H_ +#define _K3B_AIFF_DECODER_H_ + +#include <k3baudiodecoder.h> + +class KURL; + + +class K3bLibsndfileDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bLibsndfileDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bLibsndfileDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + bool multiFormatDecoder() const { return true; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class K3bLibsndfileDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bLibsndfileDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bLibsndfileDecoder(); + void cleanup(); + QString fileType() const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + bool seekInternal( const K3b::Msf& ); + + int decodeInternal( char* _data, int maxLen ); + + private: + bool openFile(); + + class Private; + Private* d; + +}; + +#endif diff --git a/plugins/decoder/libsndfile/k3blibsndfiledecoder.plugin b/plugins/decoder/libsndfile/k3blibsndfiledecoder.plugin new file mode 100644 index 0000000..7ae05f1 --- /dev/null +++ b/plugins/decoder/libsndfile/k3blibsndfiledecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3blibsndfiledecoder +Group=AudioDecoder +Name=K3b Libsndfile Decoder +Author=Matthieu Bedouet +Email=mbedouet@no-log.org +Version=1.0 +Comment=Decoding module to decode audio files supported by libsndfile +License=GPL diff --git a/plugins/decoder/mp3/Makefile.am b/plugins/decoder/mp3/Makefile.am new file mode 100644 index 0000000..7f74d21 --- /dev/null +++ b/plugins/decoder/mp3/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3bdevice $(taglib_includes) $(all_includes) + +kde_module_LTLIBRARIES = libk3bmaddecoder.la + +libk3bmaddecoder_la_SOURCES = k3bmad.cpp k3bmaddecoder.cpp + +libk3bmaddecoder_la_LIBADD = $(LIB_KDECORE) $(MAD_LIB) $(taglib_libs) ../../../libk3b/libk3b.la +libk3bmaddecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bmaddecoder.plugin + +METASOURCES = AUTO diff --git a/plugins/decoder/mp3/configure.in.bot b/plugins/decoder/mp3/configure.in.bot new file mode 100644 index 0000000..0ee4872 --- /dev/null +++ b/plugins/decoder/mp3/configure.in.bot @@ -0,0 +1,11 @@ +echo "" + +if test -n "$MAD_LIB"; then + echo "K3b - Mp3 decoding support (libmad): yes" +else + echo "K3b - Mp3 decoding support (libmad): no" +if test "$ac_cv_use_libmad" = "yes"; then + echo "K3b - You are missing the libmad headers and libraries." + echo "K3b - The Mp3 decoding plugin won't be compiled." +fi +fi diff --git a/plugins/decoder/mp3/configure.in.in b/plugins/decoder/mp3/configure.in.in new file mode 100644 index 0000000..fb92936 --- /dev/null +++ b/plugins/decoder/mp3/configure.in.in @@ -0,0 +1,24 @@ +dnl === libmad MPEG decoder check - begin === +AC_ARG_WITH( + libmad, + AS_HELP_STRING([--without-libmad], [build K3b without libmad support (default=no)]), + [ac_cv_use_libmad=$withval], + [ac_cv_use_libmad=yes] +) + +if test "$ac_cv_use_libmad" = "yes"; then + MAD_LIB="" + KDE_CHECK_HEADER(mad.h, [ + AC_CHECK_LIB(mad, mad_synth_frame, [ + MAD_LIB="-lmad" + AC_DEFINE(HAVE_LIBMAD,1,[defined if you have libmad headers and libraries])], + [], + $all_libraries + ) + ]) + AC_SUBST(MAD_LIB) + +fi + +AM_CONDITIONAL(include_MP3, [test -n "$MAD_LIB"]) +dnl === libmad MPeg decoder check - end === diff --git a/plugins/decoder/mp3/k3bmad.cpp b/plugins/decoder/mp3/k3bmad.cpp new file mode 100644 index 0000000..cb4cf6c --- /dev/null +++ b/plugins/decoder/mp3/k3bmad.cpp @@ -0,0 +1,359 @@ +/* + * + * $Id: k3bmad.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bmad.h" + +#include <qfile.h> +#include <kdebug.h> + + +static const int INPUT_BUFFER_SIZE = 5*8192; + + +K3bMad::K3bMad() + : m_madStructuresInitialized(false), + m_bInputError(false) +{ + madStream = new mad_stream; + madFrame = new mad_frame; + madSynth = new mad_synth; + madTimer = new mad_timer_t; + + // + // we allocate additional MAD_BUFFER_GUARD bytes to always be able to append the + // zero bytes needed for decoding the last frame. + // + m_inputBuffer = new unsigned char[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD]; +} + + +K3bMad::~K3bMad() +{ + cleanup(); + + delete madStream; + delete madFrame; + delete madSynth; + delete madTimer; + + delete [] m_inputBuffer; +} + + +bool K3bMad::open( const QString& filename ) +{ + cleanup(); + + m_bInputError = false; + m_channels = m_sampleRate = 0; + + m_inputFile.setName( filename ); + + if( !m_inputFile.open( IO_ReadOnly ) ) { + kdError() << "(K3bMad) could not open file " << m_inputFile.name() << endl; + return false; + } + + initMad(); + + memset( m_inputBuffer, 0, INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD ); + + return true; +} + + +bool K3bMad::inputError() const +{ + return m_bInputError; +} + + +bool K3bMad::fillStreamBuffer() +{ + /* The input bucket must be filled if it becomes empty or if + * it's the first execution of the loop. + */ + if( madStream->buffer == 0 || madStream->error == MAD_ERROR_BUFLEN ) { + if( eof() ) + return false; + + long readSize, remaining; + unsigned char* readStart; + + if( madStream->next_frame != 0 ) { + remaining = madStream->bufend - madStream->next_frame; + memmove( m_inputBuffer, madStream->next_frame, remaining ); + readStart = m_inputBuffer + remaining; + readSize = INPUT_BUFFER_SIZE - remaining; + } + else { + readSize = INPUT_BUFFER_SIZE; + readStart = m_inputBuffer; + remaining = 0; + } + + // Fill-in the buffer. + Q_LONG result = m_inputFile.readBlock( (char*)readStart, readSize ); + if( result < 0 ) { + kdDebug() << "(K3bMad) read error on bitstream)" << endl; + m_bInputError = true; + return false; + } + else if( result == 0 ) { + kdDebug() << "(K3bMad) end of input stream" << endl; + return false; + } + else { + readStart += result; + + if( eof() ) { + kdDebug() << "(K3bMad::fillStreamBuffer) MAD_BUFFER_GUARD" << endl; + memset( readStart, 0, MAD_BUFFER_GUARD ); + result += MAD_BUFFER_GUARD; + } + + // Pipe the new buffer content to libmad's stream decoder facility. + mad_stream_buffer( madStream, m_inputBuffer, result + remaining ); + madStream->error = MAD_ERROR_NONE; + } + } + + return true; +} + + +bool K3bMad::skipTag() +{ + // skip the tag at the beginning of the file + m_inputFile.at( 0 ); + + // + // now check if the file starts with an id3 tag and skip it if so + // + char buf[4096]; + int bufLen = 4096; + if( m_inputFile.readBlock( buf, bufLen ) < bufLen ) { + kdDebug() << "(K3bMad) unable to read " << bufLen << " bytes from " + << m_inputFile.name() << endl; + return false; + } + + if( ( buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3' ) && + ( (unsigned short)buf[3] < 0xff && (unsigned short)buf[4] < 0xff ) ) { + // do we have a footer? + bool footer = (buf[5] & 0x10); + + // the size is saved as a synched int meaning bit 7 is always cleared to 0 + unsigned int size = + ( (buf[6] & 0x7f) << 21 ) | + ( (buf[7] & 0x7f) << 14 ) | + ( (buf[8] & 0x7f) << 7) | + (buf[9] & 0x7f); + unsigned int offset = size + 10; + if( footer ) + offset += 10; + + kdDebug() << "(K3bMad) skipping past ID3 tag to " << offset << endl; + + // skip the id3 tag + if( !m_inputFile.at(offset) ) { + kdDebug() << "(K3bMad) " << m_inputFile.name() + << ": couldn't seek to " << offset << endl; + return false; + } + } + else { + // reset file + return m_inputFile.at( 0 ); + } + + return true; +} + + +bool K3bMad::seekFirstHeader() +{ + // + // A lot of mp3 files start with a lot of junk which confuses mad. + // We "allow" an mp3 file to start with at most 1 KB of junk. This is just + // some random value since we do not want to search the hole file. That would + // take way to long for non-mp3 files. + // + bool headerFound = findNextHeader(); + QIODevice::Offset inputPos = streamPos(); + while( !headerFound && + !m_inputFile.atEnd() && + streamPos() <= inputPos+1024 ) { + headerFound = findNextHeader(); + } + + // seek back to the begin of the frame + if( headerFound ) { + int streamSize = madStream->bufend - madStream->buffer; + int bytesToFrame = madStream->this_frame - madStream->buffer; + m_inputFile.at( m_inputFile.at() - streamSize + bytesToFrame ); + + kdDebug() << "(K3bMad) found first header at " << m_inputFile.at() << endl; + } + + // reset the stream to make sure mad really starts decoding at out seek position + mad_stream_finish( madStream ); + mad_stream_init( madStream ); + + return headerFound; +} + + +bool K3bMad::eof() const +{ + return m_inputFile.atEnd(); +} + + +QIODevice::Offset K3bMad::inputPos() const +{ + return m_inputFile.at(); +} + + +QIODevice::Offset K3bMad::streamPos() const +{ + return inputPos() - (madStream->bufend - madStream->this_frame + 1); +} + + +bool K3bMad::inputSeek( QIODevice::Offset pos ) +{ + return m_inputFile.at( pos ); +} + + +void K3bMad::initMad() +{ + if( !m_madStructuresInitialized ) { + mad_stream_init( madStream ); + mad_timer_reset( madTimer ); + mad_frame_init( madFrame ); + mad_synth_init( madSynth ); + + m_madStructuresInitialized = true; + } +} + + +void K3bMad::cleanup() +{ + if( m_inputFile.isOpen() ) { + kdDebug() << "(K3bMad) cleanup at offset: " + << "Input file at: " << m_inputFile.at() << " " + << "Input file size: " << m_inputFile.size() << " " + << "stream pos: " + << ( m_inputFile.at() - (madStream->bufend - madStream->this_frame + 1) ) + << endl; + m_inputFile.close(); + } + + if( m_madStructuresInitialized ) { + mad_frame_finish( madFrame ); + mad_synth_finish( madSynth ); + mad_stream_finish( madStream ); + } + + m_madStructuresInitialized = false; +} + + +// +// LOSTSYNC could happen when mad encounters the id3 tag... +// +bool K3bMad::findNextHeader() +{ + if( !fillStreamBuffer() ) + return false; + + // + // MAD_RECOVERABLE == true: frame was read, decoding failed (about to skip frame) + // MAD_RECOVERABLE == false: frame was not read, need data + // + + if( mad_header_decode( &madFrame->header, madStream ) < 0 ) { + if( MAD_RECOVERABLE( madStream->error ) || + madStream->error == MAD_ERROR_BUFLEN ) { + return findNextHeader(); + } + else + kdDebug() << "(K3bMad::findNextHeader) error: " << mad_stream_errorstr( madStream ) << endl; + + // FIXME probably we should not do this here since we don't do it + // in the frame decoding +// if( !checkFrameHeader( &madFrame->header ) ) +// return findNextHeader(); + + return false; + } + + if( !m_channels ) { + m_channels = MAD_NCHANNELS(&madFrame->header); + m_sampleRate = madFrame->header.samplerate; + } + + mad_timer_add( madTimer, madFrame->header.duration ); + + return true; +} + + +bool K3bMad::decodeNextFrame() +{ + if( !fillStreamBuffer() ) + return false; + + if( mad_frame_decode( madFrame, madStream ) < 0 ) { + if( MAD_RECOVERABLE( madStream->error ) || + madStream->error == MAD_ERROR_BUFLEN ) { + return decodeNextFrame(); + } + + return false; + } + + if( !m_channels ) { + m_channels = MAD_NCHANNELS(&madFrame->header); + m_sampleRate = madFrame->header.samplerate; + } + + mad_timer_add( madTimer, madFrame->header.duration ); + + return true; +} + + +// +// This is from the arts mad decoder +// +bool K3bMad::checkFrameHeader( mad_header* header ) const +{ + int frameSize = MAD_NSBSAMPLES( header ) * 32; + + if( frameSize <= 0 ) + return false; + + if( m_channels && m_channels != MAD_NCHANNELS(header) ) + return false; + + return true; +} + + diff --git a/plugins/decoder/mp3/k3bmad.h b/plugins/decoder/mp3/k3bmad.h new file mode 100644 index 0000000..a4d9aae --- /dev/null +++ b/plugins/decoder/mp3/k3bmad.h @@ -0,0 +1,92 @@ +/* + * + * $Id: k3bmad.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_MAD_H_ +#define _K3B_MAD_H_ + +extern "C" { +#include <mad.h> +} + +#include <qfile.h> + + +class K3bMad +{ +public: + K3bMad(); + ~K3bMad(); + + bool open( const QString& filename ); + + /** + * @return true if the mad stream contains data + * false if there is no data left or an error occurred. + * In the latter case inputError() returns true. + */ + bool fillStreamBuffer(); + + /** + * Skip id3 tags. + * + * This will reset the input file. + */ + bool skipTag(); + + /** + * Find first frame and seek to the beginning of that frame. + * This is used to skip the junk that many mp3 files start with. + */ + bool seekFirstHeader(); + + bool eof() const; + bool inputError() const; + + /** + * Current position in theinput file. This does NOT + * care about the status of the mad stream. Use streamPos() + * in that case. + */ + QIODevice::Offset inputPos() const; + + /** + * Current absolut position of the decoder stream. + */ + QIODevice::Offset streamPos() const; + bool inputSeek( QIODevice::Offset pos ); + + void initMad(); + void cleanup(); + + bool decodeNextFrame(); + bool findNextHeader(); + bool checkFrameHeader( mad_header* header ) const; + + mad_stream* madStream; + mad_frame* madFrame; + mad_synth* madSynth; + mad_timer_t* madTimer; + +private: + QFile m_inputFile; + bool m_madStructuresInitialized; + unsigned char* m_inputBuffer; + bool m_bInputError; + + int m_channels; + int m_sampleRate; +}; + +#endif diff --git a/plugins/decoder/mp3/k3bmaddecoder.cpp b/plugins/decoder/mp3/k3bmaddecoder.cpp new file mode 100644 index 0000000..e3aef56 --- /dev/null +++ b/plugins/decoder/mp3/k3bmaddecoder.cpp @@ -0,0 +1,542 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + + +// +// Some notes on mp3: +// A mp3 Frame is always samples/samplerate seconds in length +// +// +// +// What we need are raw 16 bit stereo samples at 44100 Hz which results in 588 samples +// per block (2352 bytes: 32*588 bit). 1 second are 75 blocks. +// + +#include <config.h> + +#include "k3bmaddecoder.h" +#include "k3bmad.h" + +#include <k3bpluginfactory.h> + +#include <kurl.h> +#include <kdebug.h> +#include <klocale.h> + +#include <qstring.h> +#include <qfile.h> +#include <qvaluevector.h> + +#include <stdlib.h> +#include <cmath> +#include <cstdlib> + +#include <config.h> + +#ifdef HAVE_TAGLIB +#include <taglib/tag.h> +#include <taglib/mpegfile.h> +#endif + + +K_EXPORT_COMPONENT_FACTORY( libk3bmaddecoder, K3bPluginFactory<K3bMadDecoderFactory>( "k3bmaddecoder" ) ) + + +int K3bMadDecoder::MaxAllowedRecoverableErrors = 10; + + + +class K3bMadDecoder::MadDecoderPrivate +{ +public: + MadDecoderPrivate() + : outputBuffer(0), + outputPointer(0), + outputBufferEnd(0) { + mad_header_init( &firstHeader ); + } + + K3bMad* handle; + + QValueVector<unsigned long long> seekPositions; + + bool bOutputFinished; + + char* outputBuffer; + char* outputPointer; + char* outputBufferEnd; + + // the first frame header for technical info + mad_header firstHeader; + bool vbr; +}; + + + + +K3bMadDecoder::K3bMadDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ + d = new MadDecoderPrivate(); + d->handle = new K3bMad(); +} + + +K3bMadDecoder::~K3bMadDecoder() +{ + cleanup(); + delete d->handle; + delete d; +} + + +QString K3bMadDecoder::metaInfo( MetaDataField f ) +{ +#ifdef HAVE_TAGLIB + TagLib::MPEG::File file( QFile::encodeName( filename() ).data() ); + + if ( file.tag() ) { + switch( f ) { + case META_TITLE: + return TStringToQString( file.tag()->title() ); + case META_ARTIST: + return TStringToQString( file.tag()->artist() ); + case META_COMMENT: + return TStringToQString( file.tag()->comment() ); + default: + return QString::null; + } + } + else { + return QString::null; + } + +#else + return K3bAudioDecoder::metaInfo( f ); +#endif +} + + +bool K3bMadDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + initDecoderInternal(); + frames = countFrames(); + if( frames > 0 ) { + // we convert mono to stereo all by ourselves. :) + ch = 2; + samplerate = d->firstHeader.samplerate; + return true; + } + else + return false; +} + + +bool K3bMadDecoder::initDecoderInternal() +{ + cleanup(); + + d->bOutputFinished = false; + + if( !d->handle->open( filename() ) ) + return false; + + if( !d->handle->skipTag() ) + return false; + + if( !d->handle->seekFirstHeader() ) + return false; + + return true; +} + + +unsigned long K3bMadDecoder::countFrames() +{ + kdDebug() << "(K3bMadDecoder::countFrames)" << endl; + + unsigned long frames = 0; + bool error = false; + d->vbr = false; + bool bFirstHeaderSaved = false; + + d->seekPositions.clear(); + + while( !error && d->handle->findNextHeader() ) { + + if( !bFirstHeaderSaved ) { + bFirstHeaderSaved = true; + d->firstHeader = d->handle->madFrame->header; + } + else if( d->handle->madFrame->header.bitrate != d->firstHeader.bitrate ) + d->vbr = true; + + // + // position in stream: postion in file minus the not yet used buffer + // + unsigned long long seekPos = d->handle->inputPos() - + (d->handle->madStream->bufend - d->handle->madStream->this_frame + 1); + + // save the number of bytes to be read to decode i-1 frames at position i + // in other words: when seeking to seekPos the next decoded frame will be i + d->seekPositions.append( seekPos ); + } + + if( !d->handle->inputError() && !error ) { + // we need the length of the track to be multiple of frames (1/75 second) + float seconds = (float)d->handle->madTimer->seconds + + (float)d->handle->madTimer->fraction/(float)MAD_TIMER_RESOLUTION; + frames = (unsigned long)ceil(seconds * 75.0); + kdDebug() << "(K3bMadDecoder) length of track " << seconds << endl; + } + + cleanup(); + + kdDebug() << "(K3bMadDecoder::countFrames) end" << endl; + + return frames; +} + + +int K3bMadDecoder::decodeInternal( char* _data, int maxLen ) +{ + d->outputBuffer = _data; + d->outputBufferEnd = d->outputBuffer + maxLen; + d->outputPointer = d->outputBuffer; + + bool bOutputBufferFull = false; + + while( !bOutputBufferFull && d->handle->fillStreamBuffer() ) { + + // a mad_synth contains of the data of one mad_frame + // one mad_frame represents a mp3-frame which is always 1152 samples + // for us that means we need 4*1152 bytes of output buffer for every frame + // since one sample has 16 bit + if( d->outputBufferEnd - d->outputPointer < 4*1152 ) { + bOutputBufferFull = true; + } + else if( d->handle->decodeNextFrame() ) { + // + // Once decoded the frame is synthesized to PCM samples. No errors + // are reported by mad_synth_frame(); + // + mad_synth_frame( d->handle->madSynth, d->handle->madFrame ); + + // this fills the output buffer + if( !createPcmSamples( d->handle->madSynth ) ) { + return -1; + } + } + else if( d->handle->inputError() ) { + return -1; + } + } + + // flush the output buffer + size_t buffersize = d->outputPointer - d->outputBuffer; + + return buffersize; +} + + +unsigned short K3bMadDecoder::linearRound( mad_fixed_t fixed ) +{ + // round + fixed += (1L << ( MAD_F_FRACBITS - 16 )); + + // clip + if( fixed >= MAD_F_ONE - 1 ) + fixed = MAD_F_ONE - 1; + else if( fixed < -MAD_F_ONE ) + fixed = -MAD_F_ONE; + + // quatisize + return fixed >> (MAD_F_FRACBITS + 1 - 16 ); +} + + +bool K3bMadDecoder::createPcmSamples( mad_synth* synth ) +{ + unsigned short nsamples = synth->pcm.length; + + // this should not happen since we only decode if the + // output buffer has enough free space + if( d->outputBufferEnd - d->outputPointer < nsamples*4 ) { + kdDebug() << "(K3bMadDecoder) buffer overflow!" << endl; + return false; + } + + // now create the output + for( int i = 0; i < nsamples; i++ ) { + + /* Left channel */ + unsigned short sample = linearRound( synth->pcm.samples[0][i] ); + *(d->outputPointer++) = (sample >> 8) & 0xff; + *(d->outputPointer++) = sample & 0xff; + + /* Right channel. If the decoded stream is monophonic then + * the right output channel is the same as the left one. + */ + if( synth->pcm.channels == 2 ) + sample = linearRound( synth->pcm.samples[1][i] ); + + *(d->outputPointer++) = (sample >> 8) & 0xff; + *(d->outputPointer++) = sample & 0xff; + } // pcm conversion + + return true; +} + + +void K3bMadDecoder::cleanup() +{ + d->handle->cleanup(); +} + + +bool K3bMadDecoder::seekInternal( const K3b::Msf& pos ) +{ + // + // we need to reset the complete mad stuff + // + if( !initDecoderInternal() ) + return false; + + // + // search a position + // This is all hacking, I don't really know what I am doing here... ;) + // + double mp3FrameSecs = static_cast<double>(d->firstHeader.duration.seconds) + + static_cast<double>(d->firstHeader.duration.fraction) / static_cast<double>(MAD_TIMER_RESOLUTION); + + double posSecs = static_cast<double>(pos.totalFrames()) / 75.0; + + // seekPosition to seek after frame i + unsigned int frame = static_cast<unsigned int>( posSecs / mp3FrameSecs ); + + // Rob said: 29 frames is the theoretically max frame reservoir limit (whatever that means...) + // it seems that mad needs at most 29 frames to get ready + unsigned int frameReservoirProtect = ( frame > 29 ? 29 : frame ); + + frame -= frameReservoirProtect; + + // seek in the input file behind the already decoded data + d->handle->inputSeek( d->seekPositions[frame] ); + + kdDebug() << "(K3bMadDecoder) Seeking to frame " << frame << " with " + << frameReservoirProtect << " reservoir frames." << endl; + + // decode some frames ignoring MAD_ERROR_BADDATAPTR errors + unsigned int i = 1; + while( i <= frameReservoirProtect ) { + d->handle->fillStreamBuffer(); + if( mad_frame_decode( d->handle->madFrame, d->handle->madStream ) ) { + if( MAD_RECOVERABLE( d->handle->madStream->error ) ) { + if( d->handle->madStream->error == MAD_ERROR_BUFLEN ) + continue; + else if( d->handle->madStream->error != MAD_ERROR_BADDATAPTR ) { + kdDebug() << "(K3bMadDecoder) Seeking: recoverable mad error (" + << mad_stream_errorstr(d->handle->madStream) << ")" << endl; + continue; + } + else { + kdDebug() << "(K3bMadDecoder) Seeking: ignoring (" + << mad_stream_errorstr(d->handle->madStream) << ")" << endl; + } + } + else + return false; + } + + if( i == frameReservoirProtect ) // synth only the last frame (Rob said so ;) + mad_synth_frame( d->handle->madSynth, d->handle->madFrame ); + + ++i; + } + + return true; +} + + +QString K3bMadDecoder::fileType() const +{ + switch( d->firstHeader.layer ) { + case MAD_LAYER_I: + return "MPEG1 Layer I"; + case MAD_LAYER_II: + return "MPEG1 Layer II"; + case MAD_LAYER_III: + return "MPEG1 Layer III"; + default: + return "Mp3"; + } +} + +QStringList K3bMadDecoder::supportedTechnicalInfos() const +{ + return QStringList::split( ";", + i18n("Channels") + ";" + + i18n("Sampling Rate") + ";" + + i18n("Bitrate") + ";" + + i18n("Layer") + ";" + + i18n("Emphasis") + ";" + + i18n("Copyright") + ";" + + i18n("Original") + ";" + + i18n("CRC") ); +} + + +QString K3bMadDecoder::technicalInfo( const QString& name ) const +{ + if( name == i18n("Channels") ) { + switch( d->firstHeader.mode ) { + case MAD_MODE_SINGLE_CHANNEL: + return i18n("Mono"); + case MAD_MODE_DUAL_CHANNEL: + return i18n("Dual"); + case MAD_MODE_JOINT_STEREO: + return i18n("Joint Stereo"); + case MAD_MODE_STEREO: + return i18n("Stereo"); + default: + return "?"; + } + } + else if( name == i18n("Sampling Rate") ) + return i18n("%1 Hz").arg(d->firstHeader.samplerate); + else if( name == i18n("Bitrate") ) { + if( d->vbr ) + return i18n("VBR"); + else + return i18n("%1 bps").arg(d->firstHeader.bitrate); + } + else if( name == i18n("Layer") ){ + switch( d->firstHeader.layer ) { + case MAD_LAYER_I: + return "I"; + case MAD_LAYER_II: + return "II"; + case MAD_LAYER_III: + return "III"; + default: + return "?"; + } + } + else if( name == i18n("Emphasis") ) { + switch( d->firstHeader.emphasis ) { + case MAD_EMPHASIS_NONE: + return i18n("None"); + case MAD_EMPHASIS_50_15_US: + return i18n("50/15 ms"); + case MAD_EMPHASIS_CCITT_J_17: + return i18n("CCITT J.17"); + default: + return i18n("Unknown"); + } + } + else if( name == i18n("Copyright") ) + return ( d->firstHeader.flags & MAD_FLAG_COPYRIGHT ? i18n("Yes") : i18n("No") ); + else if( name == i18n("Original") ) + return ( d->firstHeader.flags & MAD_FLAG_ORIGINAL ? i18n("Yes") : i18n("No") ); + else if( name == i18n("CRC") ) + return ( d->firstHeader.flags & MAD_FLAG_PROTECTION ? i18n("Yes") : i18n("No") ); + else + return QString::null; +} + + +K3bMadDecoderFactory::K3bMadDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bMadDecoderFactory::~K3bMadDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bMadDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bMadDecoder( parent, name ); +} + + +bool K3bMadDecoderFactory::canDecode( const KURL& url ) +{ + // + // HACK: + // + // I am simply no good at this and this detection code is no good as well + // It always takes waves for mp3 files so we introduce this hack to + // filter out wave files. :( + // + QFile f( url.path() ); + if( !f.open( IO_ReadOnly ) ) + return false; + char buffer[12]; + if( f.readBlock( buffer, 12 ) != 12 ) + return false; + if( !qstrncmp( buffer, "RIFF", 4 ) && + !qstrncmp( buffer + 8, "WAVE", 4 ) ) + return false; + f.close(); + + + K3bMad handle; + if( !handle.open( url.path() ) ) + return false; + + handle.skipTag(); + if( !handle.seekFirstHeader() ) + return false; + + if( handle.findNextHeader() ) { + int c = MAD_NCHANNELS( &handle.madFrame->header ); + int layer = handle.madFrame->header.layer; + unsigned int s = handle.madFrame->header.samplerate; + + // + // find 4 more mp3 headers (random value since 2 was not enough) + // This way we get most of the mp3 files while sorting out + // for example wave files. + // + int cnt = 1; + while( handle.findNextHeader() ) { + // compare the found headers + if( MAD_NCHANNELS( &handle.madFrame->header ) == c && + handle.madFrame->header.layer == layer && + handle.madFrame->header.samplerate == s ) { + // only support layer III for now since otherwise some wave files + // are taken for layer I + if( ++cnt >= 5 ) { + kdDebug() << "(K3bMadDecoder) valid mpeg 1 layer " << layer + << " file with " << c << " channels and a samplerate of " + << s << endl; + return ( layer == MAD_LAYER_III ); + } + } + else + break; + } + } + + kdDebug() << "(K3bMadDecoder) unsupported format: " << url.path() << endl; + + return false; +} + +#include "k3bmaddecoder.moc" diff --git a/plugins/decoder/mp3/k3bmaddecoder.h b/plugins/decoder/mp3/k3bmaddecoder.h new file mode 100644 index 0000000..91e0f6c --- /dev/null +++ b/plugins/decoder/mp3/k3bmaddecoder.h @@ -0,0 +1,79 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef K3BMP3MODULE_H +#define K3BMP3MODULE_H + + +#include <k3baudiodecoder.h> + +extern "C" { +#include <mad.h> +} + + +class K3bMadDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bMadDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bMadDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class K3bMadDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bMadDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bMadDecoder(); + + QString metaInfo( MetaDataField ); + + void cleanup(); + + bool seekInternal( const K3b::Msf& ); + + QString fileType() const; + QStringList supportedTechnicalInfos() const; + QString technicalInfo( const QString& ) const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + + int decodeInternal( char* _data, int maxLen ); + + private: + unsigned long countFrames(); + inline unsigned short linearRound( mad_fixed_t fixed ); + bool createPcmSamples( mad_synth* ); + + static int MaxAllowedRecoverableErrors; + + class MadDecoderPrivate; + MadDecoderPrivate* d; +}; + +#endif diff --git a/plugins/decoder/mp3/k3bmaddecoder.plugin b/plugins/decoder/mp3/k3bmaddecoder.plugin new file mode 100644 index 0000000..69fbbb8 --- /dev/null +++ b/plugins/decoder/mp3/k3bmaddecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bmaddecoder +Group=AudioDecoder +Name=K3b MAD Decoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=3.1 +Comment=Decoding module to decode MPEG 1 Layer III files +License=GPL diff --git a/plugins/decoder/musepack/Makefile.am b/plugins/decoder/musepack/Makefile.am new file mode 100644 index 0000000..beb7d63 --- /dev/null +++ b/plugins/decoder/musepack/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/plugin \ + -I$(srcdir)/../../../libk3b/core \ + -I$(srcdir)/../../../libk3bdevice \ + $(all_includes) + +kde_module_LTLIBRARIES = libk3bmpcdecoder.la + +libk3bmpcdecoder_la_SOURCES = k3bmpcdecoder.cpp k3bmpcwrapper.cpp + +libk3bmpcdecoder_la_LIBADD = ../../../libk3b/libk3b.la $(MPC_LIBS) +libk3bmpcdecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bmpcdecoder.plugin + +METASOURCES = AUTO diff --git a/plugins/decoder/musepack/configure.in.bot b/plugins/decoder/musepack/configure.in.bot new file mode 100644 index 0000000..8fb871b --- /dev/null +++ b/plugins/decoder/musepack/configure.in.bot @@ -0,0 +1,12 @@ +echo "" + +if test x$have_mpc = xyes; then + echo "K3b - Musepack support: yes" +else + echo "K3b - Musepack support: no" +if test "$ac_cv_use_mpc" = "yes"; then + echo "K3b - You are missing the Musepack headers and libraries >= 1.1." + echo "K3b - The Musepack audio decoding plugin won't be compiled." +fi +fi + diff --git a/plugins/decoder/musepack/configure.in.in b/plugins/decoder/musepack/configure.in.in new file mode 100644 index 0000000..42d24c0 --- /dev/null +++ b/plugins/decoder/musepack/configure.in.in @@ -0,0 +1,52 @@ +dnl --------- MUSEPACK CHECK --------------- + +AC_ARG_WITH( + musepack, + AS_HELP_STRING( + [--without-musepack], + [build K3b without Musepack audio support (default=no)]), + [ac_cv_use_mpc=$withval], + [ac_cv_use_mpc=yes] +) + +have_mpc=no +if test "$ac_cv_use_mpc" = "yes"; then + + dnl - search for both the new and the old naming - + + KDE_CHECK_HEADERS(mpcdec/mpcdec.h, [ + AC_CHECK_LIB(mpcdec, mpc_decoder_setup, [ + have_mpc=yes + MPC_LIBS="-lmpcdec" + AC_DEFINE( + MPC_HEADER_FILE, + <mpcdec/mpcdec.h>, + [The header to include for MPC decoding.]) + ], + [], [], []) + ]) + + if test "$have_mpc" = "no"; then + KDE_CHECK_HEADERS(musepack/musepack.h, [ + AC_CHECK_LIB(musepack, mpc_decoder_setup, [ + have_mpc=yes + MPC_LIBS="-lmusepack" + AC_DEFINE( + MPC_HEADER_FILE, + <musepack/musepack.h>, + [The header to include for MPC decoding.] + ) + AC_DEFINE( + mpc_bool_t, + BOOL, + [backwards compatibility stuff] + ) + ], [], []) + ]) + fi +fi +AC_SUBST(MPC_LIBS) + +AM_CONDITIONAL(include_MPC, [test x$have_mpc = xyes]) + +dnl --------- MUSEPACK CHECK END ----------- diff --git a/plugins/decoder/musepack/k3bmpcdecoder.cpp b/plugins/decoder/musepack/k3bmpcdecoder.cpp new file mode 100644 index 0000000..0cdf644 --- /dev/null +++ b/plugins/decoder/musepack/k3bmpcdecoder.cpp @@ -0,0 +1,111 @@ +/* + * + * $Id: k3bmpcdecoder.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bmpcdecoder.h" +#include "k3bmpcwrapper.h" + +#include <k3bpluginfactory.h> + +#include <klocale.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3bmpcdecoder, K3bPluginFactory<K3bMpcDecoderFactory>( "libk3bmpcdecoder" ) ) + + +K3bMpcDecoderFactory::K3bMpcDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bMpcDecoderFactory::~K3bMpcDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bMpcDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bMpcDecoder( parent, name ); +} + + +bool K3bMpcDecoderFactory::canDecode( const KURL& url ) +{ + K3bMpcWrapper w; + return w.open( url.path() ); +} + + + + + + +K3bMpcDecoder::K3bMpcDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ + m_mpc = new K3bMpcWrapper(); +} + + +K3bMpcDecoder::~K3bMpcDecoder() +{ + delete m_mpc; +} + + +QString K3bMpcDecoder::fileType() const +{ + return i18n("Musepack"); +} + + +bool K3bMpcDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + if( m_mpc->open( filename() ) ) { + frames = m_mpc->length(); + samplerate = m_mpc->samplerate(); + ch = m_mpc->channels(); + + // call addTechnicalInfo and addMetaInfo here + + return true; + } + else + return false; +} + + +bool K3bMpcDecoder::initDecoderInternal() +{ + return m_mpc->open( filename() ); +} + + +bool K3bMpcDecoder::seekInternal( const K3b::Msf& msf ) +{ + return m_mpc->seek( msf ); +} + + +int K3bMpcDecoder::decodeInternal( char* _data, int maxLen ) +{ + return m_mpc->decode( _data, maxLen ); +} + + +#include "k3bmpcdecoder.moc" diff --git a/plugins/decoder/musepack/k3bmpcdecoder.h b/plugins/decoder/musepack/k3bmpcdecoder.h new file mode 100644 index 0000000..74dc509 --- /dev/null +++ b/plugins/decoder/musepack/k3bmpcdecoder.h @@ -0,0 +1,62 @@ +/* + * + * $Id: k3bmpcdecoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_MPC_DECODER_H_ +#define _K3B_MPC_DECODER_H_ + +#include <k3baudiodecoder.h> + +class K3bMpcWrapper; + + +class K3bMpcDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bMpcDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bMpcDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class K3bMpcDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bMpcDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bMpcDecoder(); + + QString fileType() const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + bool seekInternal( const K3b::Msf& ); + + int decodeInternal( char* _data, int maxLen ); + + private: + K3bMpcWrapper* m_mpc; +}; + +#endif diff --git a/plugins/decoder/musepack/k3bmpcdecoder.plugin b/plugins/decoder/musepack/k3bmpcdecoder.plugin new file mode 100644 index 0000000..6e350dc --- /dev/null +++ b/plugins/decoder/musepack/k3bmpcdecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bmpcdecoder +Group=AudioDecoder +Name=K3b Musepack Decoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=1.0 +Comment=Decoding module to decode Musepack audio files +License=GPL diff --git a/plugins/decoder/musepack/k3bmpcwrapper.cpp b/plugins/decoder/musepack/k3bmpcwrapper.cpp new file mode 100644 index 0000000..9f54b28 --- /dev/null +++ b/plugins/decoder/musepack/k3bmpcwrapper.cpp @@ -0,0 +1,193 @@ +/* + * + * $Id: k3bmpcwrapper.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bmpcwrapper.h" + +#include <kdebug.h> +#include <qfile.h> + + +mpc_int32_t read_impl( void* data, void* ptr, mpc_int32_t size ) +{ + QFile* input = static_cast<QFile*>( data ); + return input->readBlock( (char*)ptr, size ); +} + + +mpc_bool_t seek_impl( void* data, mpc_int32_t offset ) +{ + QFile* input = static_cast<QFile*>( data ); + return input->at( offset ); +} + +mpc_int32_t tell_impl( void* data ) +{ + QFile* input = static_cast<QFile*>( data ); + return input->at(); +} + +mpc_int32_t get_size_impl( void* data ) +{ + QFile* input = static_cast<QFile*>( data ); + return input->size(); +} + +mpc_bool_t canseek_impl( void* ) +{ + return true; +} + + +#ifdef MPC_FIXED_POINT +static int shift_signed( MPC_SAMPLE_FORMAT val, int shift ) +{ + if(shift > 0) + val <<= shift; + else if(shift < 0) + val >>= -shift; + return (int) val; +} +#endif + + +K3bMpcWrapper::K3bMpcWrapper() +{ + m_input = new QFile(); + + m_reader = new mpc_reader; + m_reader->read = read_impl; + m_reader->seek = seek_impl; + m_reader->tell = tell_impl; + m_reader->get_size = get_size_impl; + m_reader->canseek = canseek_impl; + m_reader->data = m_input; + + m_decoder = new mpc_decoder; + + m_info = new mpc_streaminfo; +} + + +K3bMpcWrapper::~K3bMpcWrapper() +{ + close(); + + delete m_reader; + delete m_decoder; + delete m_info; + delete m_input; +} + + +bool K3bMpcWrapper::open( const QString& filename ) +{ + close(); + + m_input->setName( filename ); + + if( m_input->open( IO_ReadOnly ) ) { + mpc_streaminfo_init( m_info ); + if( mpc_streaminfo_read( m_info, m_reader ) != ERROR_CODE_OK ) { + kdDebug() << "(K3bMpcWrapper) Not a valid musepack file: \"" << filename << "\"" << endl; + return false; + } + else { + mpc_decoder_setup( m_decoder, m_reader ); + if( !mpc_decoder_initialize( m_decoder, m_info ) ) { + kdDebug() << "(K3bMpcWrapper) failed to initialize the Musepack decoder." << endl; + close(); + return false; + } + else { + kdDebug() << "(K3bMpcWrapper) valid musepack file. " + << channels() << " Channels and Samplerate: " << samplerate() << endl; + return true; + } + } + } + else + return false; +} + + +void K3bMpcWrapper::close() +{ + m_input->close(); +} + + +int K3bMpcWrapper::decode( char* data, int max ) +{ + // FIXME: make this a member variable + MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH]; + + unsigned int samples = mpc_decoder_decode( m_decoder, sample_buffer, 0, 0 ); + + if( samples*channels()*2 > (unsigned int)max ) { + kdDebug() << "(K3bMpcWrapper) buffer not big enough." << endl; + return -1; + } + + static const unsigned int bps = 16; + static const int clip_min = -1 << (bps - 1); + static const int clip_max = (1 << (bps - 1)) - 1; + static const int float_scale = 1 << (bps - 1); + + for( unsigned int n = 0; n < samples*channels(); ++n ) { + int val = 0; + +#ifdef MPC_FIXED_POINT + val = shift_signed( sample_buffer[n], + bps - MPC_FIXED_POINT_SCALE_SHIFT); +#else + val = (int)(sample_buffer[n] * float_scale); +#endif + + if( val < clip_min ) + val = clip_min; + else if( val > clip_max ) + val = clip_max; + + data[2*n] = (val>>8) & 0xff; + data[2*n+1] = val & 0xff; + } + + return samples*channels()*2; +} + + +bool K3bMpcWrapper::seek( const K3b::Msf& msf ) +{ + return mpc_decoder_seek_seconds( m_decoder, (double)msf.totalFrames()/75.0 ); +} + + +K3b::Msf K3bMpcWrapper::length() const +{ + return K3b::Msf::fromSeconds( mpc_streaminfo_get_length( m_info ) ); +} + + +int K3bMpcWrapper::samplerate() const +{ + return m_info->sample_freq; +} + + +unsigned int K3bMpcWrapper::channels() const +{ + return m_info->channels; +} + diff --git a/plugins/decoder/musepack/k3bmpcwrapper.h b/plugins/decoder/musepack/k3bmpcwrapper.h new file mode 100644 index 0000000..743e25f --- /dev/null +++ b/plugins/decoder/musepack/k3bmpcwrapper.h @@ -0,0 +1,58 @@ +/* + * + * $Id: k3bmpcwrapper.h 630384 2007-02-05 09:33:17Z mlaurent $ + * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_MPC_WRAPPER_H_ +#define _K3B_MPC_WRAPPER_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qstring.h> + +#include <k3bmsf.h> + +#include MPC_HEADER_FILE + +class QFile; + + +class K3bMpcWrapper +{ + public: + K3bMpcWrapper(); + ~K3bMpcWrapper(); + + bool open( const QString& filename ); + void close(); + + int decode( char*, int max ); + + bool seek( const K3b::Msf& ); + + K3b::Msf length() const; + int samplerate() const; + unsigned int channels() const; + + QFile* input() const { return m_input; } + + private: + QFile* m_input; + mpc_reader* m_reader; + mpc_decoder* m_decoder; + mpc_streaminfo* m_info; +}; + +#endif diff --git a/plugins/decoder/ogg/Makefile.am b/plugins/decoder/ogg/Makefile.am new file mode 100644 index 0000000..6705be2 --- /dev/null +++ b/plugins/decoder/ogg/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3boggvorbisdecoder.la + +libk3boggvorbisdecoder_la_SOURCES = k3boggvorbisdecoder.cpp + +libk3boggvorbisdecoder_la_LIBADD = ../../../libk3b/libk3b.la -logg -lvorbis -lvorbisfile +libk3boggvorbisdecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3boggvorbisdecoder.plugin + +METASOURCES = AUTO diff --git a/plugins/decoder/ogg/configure.in.bot b/plugins/decoder/ogg/configure.in.bot new file mode 100644 index 0000000..b27ab18 --- /dev/null +++ b/plugins/decoder/ogg/configure.in.bot @@ -0,0 +1,11 @@ +echo "" + +if test x$ogg_vorbis = xyes; then + echo "K3b - Ogg Vorbis support: yes" +else + echo "K3b - Ogg Vorbis support: no" +if test "$ac_cv_use_oggvorbis" = "yes"; then + echo "K3b - You are missing the Ogg-Vorbis headers and libraries." + echo "K3b - The Ogg Vorbis decoding and encoding plugins won't be compiled." +fi +fi diff --git a/plugins/decoder/ogg/configure.in.in b/plugins/decoder/ogg/configure.in.in new file mode 100644 index 0000000..69b1b9c --- /dev/null +++ b/plugins/decoder/ogg/configure.in.in @@ -0,0 +1,32 @@ +dnl === Ogg Vorbis Test - Begin === +AC_ARG_WITH( + oggvorbis, + AS_HELP_STRING([--without-oggvorbis], [build K3b without OggVorbis support (default=no)]), + [ac_cv_use_oggvorbis=$withval], + [ac_cv_use_oggvorbis=yes] +) + +if test "$ac_cv_use_oggvorbis" = "yes"; then + + AC_MSG_CHECKING(for ogg/vorbis headers) + ogg_vorbis=no + AC_TRY_COMPILE([ + #include <vorbis/codec.h> + #include <vorbis/vorbisfile.h> + ],[ + ],[ + ogg_vorbis=yes + ]) + AC_MSG_RESULT($ogg_vorbis) + if test x$ogg_vorbis = xyes; then + dnl we need the ogg_vorbis_lib because otherwise we override LIBS ! + AC_CHECK_LIB(vorbisfile,ov_open,ogg_vorbis_lib=yes, + ogg_vorbis=no,[$all_libraries -lvorbisfile -lvorbis -logg]) + fi + if test x$ogg_vorbis = xyes; then + AC_DEFINE(OGG_VORBIS,1,[Define if you have ogg/vorbis installed]) + fi +fi + +AM_CONDITIONAL(include_OGG, [test x$ogg_vorbis = xyes]) +dnl === Ogg Vorbis Test - End === diff --git a/plugins/decoder/ogg/k3boggvorbisdecoder.cpp b/plugins/decoder/ogg/k3boggvorbisdecoder.cpp new file mode 100644 index 0000000..15ca665 --- /dev/null +++ b/plugins/decoder/ogg/k3boggvorbisdecoder.cpp @@ -0,0 +1,252 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3boggvorbisdecoder.h" + +#include <k3bpluginfactory.h> + +#include <qfile.h> +#include <qstringlist.h> + +#include <kurl.h> +#include <kdebug.h> +#include <klocale.h> + +#include <stdio.h> +#include <stdlib.h> +#include <vorbis/codec.h> +#include <vorbis/vorbisfile.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3boggvorbisdecoder, K3bPluginFactory<K3bOggVorbisDecoderFactory>( "libk3boggvorbisdecoder" ) ) + + +class K3bOggVorbisDecoder::Private +{ +public: + Private() + : vInfo(0), + vComment(0), + isOpen(false) { + } + + OggVorbis_File oggVorbisFile; + vorbis_info* vInfo; + vorbis_comment* vComment; + bool isOpen; +}; + + +K3bOggVorbisDecoder::K3bOggVorbisDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ + d = new Private(); +} + + +K3bOggVorbisDecoder::~K3bOggVorbisDecoder() +{ + delete d; +} + + +bool K3bOggVorbisDecoder::openOggVorbisFile() +{ + if( !d->isOpen ) { + FILE* file = fopen( QFile::encodeName(filename()), "r" ); + if( !file ) { + kdDebug() << "(K3bOggVorbisDecoder) Could not open file " << filename() << endl; + return false; + } + else if( ov_open( file, &d->oggVorbisFile, 0, 0 ) ) { + kdDebug() << "(K3bOggVorbisDecoder) " << filename() + << " seems not to to be an ogg vorbis file." << endl; + fclose( file ); + return false; + } + } + + d->isOpen = true; + return true; +} + + +bool K3bOggVorbisDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + cleanup(); + + if( openOggVorbisFile() ) { + // check length of track + double seconds = ov_time_total( &d->oggVorbisFile, -1 ); + if( seconds == OV_EINVAL ) { + kdDebug() << "(K3bOggVorbisDecoder) Could not determine length of file " << filename() << endl; + cleanup(); + return false; + } + else { + + d->vInfo = ov_info( &d->oggVorbisFile, -1 /* current bitstream */ ); + d->vComment = ov_comment( &d->oggVorbisFile, -1 ); + + // add meta tags + for( int i = 0; i < d->vComment->comments; ++i ) { + QString comment = QString::fromUtf8( d->vComment->user_comments[i] ); + QStringList values = QStringList::split( "=", comment ); + if( values.count() > 1 ) { + if( values[0].lower() == "title" ) + addMetaInfo( META_TITLE, values[1] ); + else if( values[0].lower() == "artist" ) + addMetaInfo( META_ARTIST, values[1] ); + else if( values[0].lower() == "description" ) + addMetaInfo( META_COMMENT, values[1] ); + } + } + + + // add technical infos + addTechnicalInfo( i18n("Version"), QString::number(d->vInfo->version) ); + addTechnicalInfo( i18n("Channels"), QString::number(d->vInfo->channels) ); + addTechnicalInfo( i18n("Sampling Rate"), i18n("%1 Hz").arg(d->vInfo->rate) ); + if( d->vInfo->bitrate_upper > 0 ) + addTechnicalInfo( i18n("Bitrate Upper"), i18n( "%1 bps" ).arg(d->vInfo->bitrate_upper) ); + if( d->vInfo->bitrate_nominal > 0 ) + addTechnicalInfo( i18n("Bitrate Nominal"), i18n( "%1 bps" ).arg(d->vInfo->bitrate_nominal) ); + if( d->vInfo->bitrate_lower > 0 ) + addTechnicalInfo( i18n("Bitrate Lower"), i18n( "%1 bps" ).arg(d->vInfo->bitrate_lower) ); + + frames = K3b::Msf::fromSeconds(seconds); + samplerate = d->vInfo->rate; + ch = d->vInfo->channels; + + cleanup(); + + return true; + } + } + else + return false; +} + + +bool K3bOggVorbisDecoder::initDecoderInternal() +{ + cleanup(); + return openOggVorbisFile(); +} + + +int K3bOggVorbisDecoder::decodeInternal( char* data, int maxLen ) +{ + int bitStream = 0; + long bytesRead = ov_read( &d->oggVorbisFile, + data, + maxLen, // max length to be read + 1, // big endian + 2, // word size: 16-bit samples + 1, // signed + &bitStream ); // current bitstream + + if( bitStream != 0 ) { + kdDebug() << "(K3bOggVorbisDecoder) bitstream != 0. Multible bitstreams not supported." << endl; + return -1; + } + + else if( bytesRead == OV_HOLE ) { + kdDebug() << "(K3bOggVorbisDecoder) OV_HOLE" << endl; + // recursive new try + return decodeInternal( data, maxLen ); + } + + else if( bytesRead < 0 ) { + kdDebug() << "(K3bOggVorbisDecoder) Error: " << bytesRead << endl; + return -1; + } + + else if( bytesRead == 0 ) { + kdDebug() << "(K3bOggVorbisDecoder) successfully finished decoding." << endl; + return 0; + } + + else { + return bytesRead; + } +} + + +void K3bOggVorbisDecoder::cleanup() +{ + if( d->isOpen ) + ov_clear( &d->oggVorbisFile ); + d->isOpen = false; + d->vComment = 0; + d->vInfo = 0; +} + + +bool K3bOggVorbisDecoder::seekInternal( const K3b::Msf& pos ) +{ + return ( ov_pcm_seek( &d->oggVorbisFile, pos.pcmSamples() ) == 0 ); +} + + +QString K3bOggVorbisDecoder::fileType() const +{ + return i18n("Ogg-Vorbis"); +} + + +K3bOggVorbisDecoderFactory::K3bOggVorbisDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bOggVorbisDecoderFactory::~K3bOggVorbisDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bOggVorbisDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bOggVorbisDecoder( parent, name ); +} + + +bool K3bOggVorbisDecoderFactory::canDecode( const KURL& url ) +{ + FILE* file = fopen( QFile::encodeName(url.path()), "r" ); + if( !file ) { + kdDebug() << "(K3bOggVorbisDecoder) Could not open file " << url.path() << endl; + return false; + } + + OggVorbis_File of; + + if( ov_open( file, &of, 0, 0 ) ) { + fclose( file ); + kdDebug() << "(K3bOggVorbisDecoder) not an Ogg-Vorbis file: " << url.path() << endl; + return false; + } + + ov_clear( &of ); + + return true; +} + + +#include "k3boggvorbisdecoder.moc" diff --git a/plugins/decoder/ogg/k3boggvorbisdecoder.h b/plugins/decoder/ogg/k3boggvorbisdecoder.h new file mode 100644 index 0000000..20ae094 --- /dev/null +++ b/plugins/decoder/ogg/k3boggvorbisdecoder.h @@ -0,0 +1,72 @@ +/* + * + * $Id: k3boggvorbisdecoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_OGGVORBIS_DECODER_H_ +#define _K3B_OGGVORBIS_DECODER_H_ + + +#include <k3baudiodecoder.h> + +class KURL; + + +class K3bOggVorbisDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bOggVorbisDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bOggVorbisDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +/** + *@author Sebastian Trueg + */ +class K3bOggVorbisDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bOggVorbisDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bOggVorbisDecoder(); + + void cleanup(); + + QString fileType() const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + bool seekInternal( const K3b::Msf& ); + + int decodeInternal( char* _data, int maxLen ); + + private: + bool openOggVorbisFile(); + + class Private; + Private* d; +}; + +#endif diff --git a/plugins/decoder/ogg/k3boggvorbisdecoder.plugin b/plugins/decoder/ogg/k3boggvorbisdecoder.plugin new file mode 100644 index 0000000..0f1c48e --- /dev/null +++ b/plugins/decoder/ogg/k3boggvorbisdecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3boggvorbisdecoder +Group=AudioDecoder +Name=K3b Ogg Vorbis Decoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=3.0 +Comment=Decoding module to decode Ogg Vorbis files +License=GPL diff --git a/plugins/decoder/skeleton.cpp b/plugins/decoder/skeleton.cpp new file mode 100644 index 0000000..940814b --- /dev/null +++ b/plugins/decoder/skeleton.cpp @@ -0,0 +1,101 @@ +/* + * + * $Id: skeleton.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3b<name>decoder.h" + +#include <k3bpluginfactory.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3b<name>decoder, K3bPluginFactory<K3b<name>DecoderFactory>( "libk3b<name>decoder" ) ) + + +K3b<name>DecoderFactory::K3b<name>DecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3b<name>DecoderFactory::~K3b<name>DecoderFactory() +{ +} + + +K3bAudioDecoder* K3b<name>DecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3b<name>Decoder( parent, name ); +} + + +bool K3b<name>DecoderFactory::canDecode( const KURL& url ) +{ + // PUT YOUR CODE HERE + return false; +} + + + + + + +K3b<name>Decoder::K3b<name>Decoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ +} + + +K3b<name>Decoder::~K3b<name>Decoder() +{ +} + + +QString K3b<name>Decoder::fileType() const +{ + // PUT YOUR CODE HERE +} + + +bool K3b<name>Decoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) +{ + // PUT YOUR CODE HERE + // call addTechnicalInfo and addMetaInfo here + return false; +} + + +bool K3b<name>Decoder::initDecoderInternal() +{ + // PUT YOUR CODE HERE + return false; +} + + +bool K3b<name>Decoder::seekInternal( const K3b::Msf& ) +{ + // PUT YOUR CODE HERE + return false; +} + + +int K3b<name>Decoder::decodeInternal( char* _data, int maxLen ) +{ + // PUT YOUR CODE HERE + return -1; +} + + +#include "k3b<name>decoder.moc" diff --git a/plugins/decoder/skeleton.h b/plugins/decoder/skeleton.h new file mode 100644 index 0000000..00f5d15 --- /dev/null +++ b/plugins/decoder/skeleton.h @@ -0,0 +1,57 @@ +/* + * + * $Id: skeleton.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_<name>_DECODER_H_ +#define _K3B_<name>_DECODER_H_ + +#include <k3baudiodecoder.h> + + +class K3b<name>DecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3b<name>DecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3b<name>DecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class K3b<name>Decoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3b<name>Decoder( QObject* parent = 0, const char* name = 0 ); + ~K3b<name>Decoder(); + + QString fileType() const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); + bool initDecoderInternal(); + bool seekInternal( const K3b::Msf& ); + + int decodeInternal( char* _data, int maxLen ); +}; + +#endif diff --git a/plugins/decoder/skeleton.plugin b/plugins/decoder/skeleton.plugin new file mode 100644 index 0000000..c9bc0f4 --- /dev/null +++ b/plugins/decoder/skeleton.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=lib<name>decoder +Group=AudioDecoder +Name=K3b ??? Decoder +Author=??? +Email=??? +Version=0.1 +Comment=Decoding module to decode ??? files +License=??? diff --git a/plugins/decoder/wave/Makefile.am b/plugins/decoder/wave/Makefile.am new file mode 100644 index 0000000..5debea1 --- /dev/null +++ b/plugins/decoder/wave/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3bwavedecoder.la + +libk3bwavedecoder_la_SOURCES = k3bwavedecoder.cpp + +libk3bwavedecoder_la_LIBADD = $(LIB_KDECORE) ../../../libk3b/libk3b.la +libk3bwavedecoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bwavedecoder.plugin + +METASOURCES = AUTO diff --git a/plugins/decoder/wave/k3bwavedecoder.cpp b/plugins/decoder/wave/k3bwavedecoder.cpp new file mode 100644 index 0000000..6494e5c --- /dev/null +++ b/plugins/decoder/wave/k3bwavedecoder.cpp @@ -0,0 +1,392 @@ +/* + * + * $Id: k3bwavedecoder.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bwavedecoder.h" + +#include <k3bpluginfactory.h> + +#include <qfile.h> +#include <qcstring.h> + +#include <kdebug.h> +#include <klocale.h> + + + +K_EXPORT_COMPONENT_FACTORY( libk3bwavedecoder, K3bPluginFactory<K3bWaveDecoderFactory>( "libk3bwavedecoder" ) ) + + +static unsigned short le_a_to_u_short( unsigned char* a ) { + return ((unsigned short) + ((a[0] & 0xFF) | + (a[1] << 8 & 0xFF00)) ); +} + +static unsigned long le_a_to_u_long( unsigned char* a ) { + return ((unsigned long) + ((a[0] & 0xFF) | + (a[1] << 8 & 0xFF00) | + (a[2] << 16 & 0xFF0000) | + (a[3] << 24 & 0xFF000000)) ); +} + + +/** + * Returns the length of the wave file in bytes + * Otherwise 0 is returned. + * leave file seek pointer past WAV header. + */ +static unsigned long identifyWaveFile( QFile* f, int* samplerate = 0, int* channels = 0, int* samplesize = 0 ) +{ + typedef struct { + unsigned char ckid[4]; + unsigned char cksize[4]; + } chunk_t; + + typedef struct { + unsigned char wave[4]; + } riff_chunk; + + typedef struct { + unsigned char fmt_tag[2]; + unsigned char channels[2]; + unsigned char sample_rate[4]; + unsigned char av_byte_rate[4]; + unsigned char block_size[2]; + unsigned char bits_per_sample[2]; + } fmt_chunk; + + static const char* WAV_RIFF_MAGIC = "RIFF"; // Magic for file format + static const char* WAV_WAVE_MAGIC = "WAVE"; // Magic for Waveform Audio + static const char* WAV_FMT_MAGIC = "fmt "; // Start of Waveform format + static const char* WAV_DATA_MAGIC = "data"; // Start of data chunk + + chunk_t chunk; + riff_chunk riff; + fmt_chunk fmt; + + + // read riff chunk + if( f->readBlock( (char*)&chunk, sizeof(chunk) ) != sizeof(chunk) ) { + kdDebug() << "(K3bWaveDecoder) unable to read from " << f->name() << endl; + return 0; + } + if( qstrncmp( (char*)chunk.ckid, WAV_RIFF_MAGIC, 4 ) ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": not a RIFF file." << endl; + return 0; + } + + // read wave chunk + if( f->readBlock( (char*)&riff, sizeof(riff) ) != sizeof(riff) ) { + kdDebug() << "(K3bWaveDecoder) unable to read from " << f->name() << endl; + return 0; + } + if( qstrncmp( (char*)riff.wave, WAV_WAVE_MAGIC, 4 ) ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": not a WAVE file." << endl; + return 0; + } + + + // read fmt chunk + if( f->readBlock( (char*)&chunk, sizeof(chunk) ) != sizeof(chunk) ) { + kdDebug() << "(K3bWaveDecoder) unable to read from " << f->name() << endl; + return 0; + } + if( qstrncmp( (char*)chunk.ckid, WAV_FMT_MAGIC, 4 ) ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": could not find format chunk." << endl; + return 0; + } + if( f->readBlock( (char*)&fmt, sizeof(fmt) ) != sizeof(fmt) ) { + kdDebug() << "(K3bWaveDecoder) unable to read from " << f->name() << endl; + return 0; + } + if( le_a_to_u_short(fmt.fmt_tag) != 1 || + le_a_to_u_short(fmt.channels) > 2 || + ( le_a_to_u_short(fmt.bits_per_sample) != 16 && + le_a_to_u_short(fmt.bits_per_sample) != 8 ) ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": wrong format:" << endl + << " format: " << le_a_to_u_short(fmt.fmt_tag) << endl + << " channels: " << le_a_to_u_short(fmt.channels) << endl + << " samplerate: " << le_a_to_u_long(fmt.sample_rate) << endl + << " bits/sample: " << le_a_to_u_short(fmt.bits_per_sample) << endl; + return 0; + } + + int sampleRate = le_a_to_u_long(fmt.sample_rate); + int ch = le_a_to_u_short(fmt.channels); + int sampleSize = le_a_to_u_short(fmt.bits_per_sample);; + if( samplerate ) + *samplerate = sampleRate; + if( channels ) + *channels = ch; + if( samplesize ) + *samplesize = sampleSize; + + // skip all other (unknown) format chunk fields + if( !f->at( f->at() + le_a_to_u_long(chunk.cksize) - sizeof(fmt) ) ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": could not seek in file." << endl; + return 0; + } + + + // find data chunk + bool foundData = false; + while( !foundData ) { + if( f->readBlock( (char*)&chunk, sizeof(chunk) ) != sizeof(chunk) ) { + kdDebug() << "(K3bWaveDecoder) unable to read from " << f->name() << endl; + return 0; + } + + // skip chunk data of unknown chunk + if( qstrncmp( (char*)chunk.ckid, WAV_DATA_MAGIC, 4 ) ) { + kdDebug() << "(K3bWaveDecoder) skipping chunk: " << (char*)chunk.ckid << endl; + if( !f->at( f->at() + le_a_to_u_long(chunk.cksize) ) ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": could not seek in file." << endl; + return 0; + } + } + else + foundData = true; + } + + // found data chunk + unsigned long size = le_a_to_u_long(chunk.cksize); + if( f->at() + size > (unsigned long)f->size() ) { + kdDebug() << "(K3bWaveDecoder) " << f->name() << ": file length " << f->size() + << " does not match length from WAVE header " << f->at() << " + " << size + << " - using actual length." << endl; + size = (f->size() - f->at()); + } + + return size; +} + + +class K3bWaveDecoder::Private { +public: + Private() + : buffer(0), + bufferSize(0) { + } + + QFile* file; + + long headerLength; + int sampleRate; + int channels; + int sampleSize; + unsigned long size; + unsigned long alreadyRead; + + char* buffer; + int bufferSize; +}; + + +K3bWaveDecoder::K3bWaveDecoder( QObject* parent, const char* name ) + : K3bAudioDecoder( parent, name ) +{ + d = new Private(); + d->file = new QFile(); +} + + +K3bWaveDecoder::~K3bWaveDecoder() +{ + delete d->file; + delete d; +} + + +int K3bWaveDecoder::decodeInternal( char* _data, int maxLen ) +{ + int read = 0; + + maxLen = QMIN( maxLen, (int)(d->size - d->alreadyRead) ); + + if( d->sampleSize == 16 ) { + read = d->file->readBlock( _data, maxLen ); + if( read > 0 ) { + d->alreadyRead += read; + + if( read % 2 > 0 ) { + kdDebug() << "(K3bWaveDecoder) data length is not a multiple of 2! Cutting data." << endl; + read -= 1; + } + + // swap bytes + char buf; + for( int i = 0; i < read; i+=2 ) { + buf = _data[i]; + _data[i] = _data[i+1]; + _data[i+1] = buf; + } + } + } + else { + if( !d->buffer ) { + d->buffer = new char[maxLen/2]; + d->bufferSize = maxLen/2; + } + + read = d->file->readBlock( d->buffer, QMIN(maxLen/2, d->bufferSize) ); + d->alreadyRead += read; + + // stretch samples to 16 bit + from8BitTo16BitBeSigned( d->buffer, _data, read ); + + read *= 2; + } + + return read; +} + + +bool K3bWaveDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& channels ) +{ + // handling wave files is very easy... + if( initDecoderInternal() ) { + + // + // d->size is the number of bytes in the wave file + // + unsigned long size = d->size; + if( d->sampleRate != 44100 ) + size = (int)((double)size * 44100.0 / (double)d->sampleRate); + + if( d->sampleSize == 8 ) + size *= 2; + if( d->channels == 1 ) + size *= 2; + + // + // we pad to a multiple of 2352 bytes + // (the actual padding of zero data will be done by the K3bAudioDecoder class) + // + if( (size%2352) > 0 ) + size = (size/2352) + 1; + else + size = size/2352; + + frames = size; + samplerate = d->sampleRate; + channels = d->channels; + return true; + } + else + return false; +} + + +bool K3bWaveDecoder::initDecoderInternal() +{ + cleanup(); + + d->file->setName( filename() ); + if( !d->file->open( IO_ReadOnly ) ) { + kdDebug() << "(K3bWaveDecoder) could not open file." << endl; + return false; + } + + // skip the header + d->size = identifyWaveFile( d->file, &d->sampleRate, &d->channels, &d->sampleSize ); + if( d->size <= 0 ) { + kdDebug() << "(K3bWaveDecoder) no supported wave file." << endl; + cleanup(); + return false; + } + + d->headerLength = d->file->at(); + d->alreadyRead = 0; + + return true; +} + + +bool K3bWaveDecoder::seekInternal( const K3b::Msf& pos ) +{ + return( d->file->at( d->headerLength + (pos.totalFrames()*2352) ) ); +} + + +void K3bWaveDecoder::cleanup() +{ + if( d->file->isOpen() ) + d->file->close(); +} + + +QString K3bWaveDecoder::fileType() const +{ + return i18n("WAVE"); +} + + +QStringList K3bWaveDecoder::supportedTechnicalInfos() const +{ + return QStringList::split( ";", + i18n("Channels") + ";" + + i18n("Sampling Rate") + ";" + + i18n("Sample Size") ); +} + + +QString K3bWaveDecoder::technicalInfo( const QString& name ) const +{ + if( name == i18n("Channels") ) + return QString::number(d->channels); + else if( name == i18n("Sampling Rate") ) + return i18n("%1 Hz").arg(d->sampleRate); + else if( name == i18n("Sample Size") ) + return i18n("%1 bits").arg(d->sampleSize); + else + return QString::null; +} + + +K3bWaveDecoderFactory::K3bWaveDecoderFactory( QObject* parent, const char* name ) + : K3bAudioDecoderFactory( parent, name ) +{ +} + + +K3bWaveDecoderFactory::~K3bWaveDecoderFactory() +{ +} + + +K3bAudioDecoder* K3bWaveDecoderFactory::createDecoder( QObject* parent, + const char* name ) const +{ + return new K3bWaveDecoder( parent, name ); +} + + +bool K3bWaveDecoderFactory::canDecode( const KURL& url ) +{ + QFile f( url.path() ); + if( !f.open( IO_ReadOnly ) ) { + kdDebug() << "(K3bWaveDecoder) could not open file " << url.path() << endl; + return false; + } + + return (identifyWaveFile( &f ) > 0); +} + + + +#include "k3bwavedecoder.moc" diff --git a/plugins/decoder/wave/k3bwavedecoder.h b/plugins/decoder/wave/k3bwavedecoder.h new file mode 100644 index 0000000..3f92e9a --- /dev/null +++ b/plugins/decoder/wave/k3bwavedecoder.h @@ -0,0 +1,74 @@ +/* + * + * $Id: k3bwavedecoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_WAVE_DECODER_H_ +#define _K3B_WAVE_DECODER_H_ + +#include <k3baudiodecoder.h> +#include <k3b_export.h> + +#include <kurl.h> +#include <qcstring.h> + + +class QFile; + + +class LIBK3B_EXPORT K3bWaveDecoderFactory : public K3bAudioDecoderFactory +{ + Q_OBJECT + + public: + K3bWaveDecoderFactory( QObject* parent = 0, const char* name = 0 ); + ~K3bWaveDecoderFactory(); + + bool canDecode( const KURL& filename ); + + int pluginSystemVersion() const { return 3; } + + K3bAudioDecoder* createDecoder( QObject* parent = 0, + const char* name = 0 ) const; +}; + + +class LIBK3B_EXPORT K3bWaveDecoder : public K3bAudioDecoder +{ + Q_OBJECT + + public: + K3bWaveDecoder( QObject* parent = 0, const char* name = 0 ); + ~K3bWaveDecoder(); + + void cleanup(); + + bool seekInternal( const K3b::Msf& ); + + QString fileType() const; + + QStringList supportedTechnicalInfos() const; + + QString technicalInfo( const QString& ) const; + + protected: + bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& channels ); + bool initDecoderInternal(); + int decodeInternal( char* data, int maxLen ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/plugins/decoder/wave/k3bwavedecoder.plugin b/plugins/decoder/wave/k3bwavedecoder.plugin new file mode 100644 index 0000000..7b0a4f4 --- /dev/null +++ b/plugins/decoder/wave/k3bwavedecoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bwavedecoder +Group=AudioDecoder +Name=K3b Wave Decoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=3.0 +Comment=Decoding module to decode wave files +License=GPL diff --git a/plugins/encoder/Makefile.am b/plugins/encoder/Makefile.am new file mode 100644 index 0000000..35724e1 --- /dev/null +++ b/plugins/encoder/Makefile.am @@ -0,0 +1,9 @@ +if include_LAME +ENCLAMEDIR = lame +endif + +if include_OGG +ENCOGGDIR = ogg +endif + +SUBDIRS = sox external $(ENCLAMEDIR) $(ENCOGGDIR) diff --git a/plugins/encoder/external/Makefile.am b/plugins/encoder/external/Makefile.am new file mode 100644 index 0000000..a095688 --- /dev/null +++ b/plugins/encoder/external/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3bexternalencoder.la + +libk3bexternalencoder_la_SOURCES = base_k3bexternalencoderconfigwidget.ui \ + base_k3bexternalencodereditwidget.ui k3bexternalencoder.cpp \ + k3bexternalencoderconfigwidget.cpp k3bexternalencodercommand.cpp + +libk3bexternalencoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDECORE) +libk3bexternalencoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bexternalencoder.plugin + +METASOURCES = AUTO diff --git a/plugins/encoder/external/base_k3bexternalencoderconfigwidget.ui b/plugins/encoder/external/base_k3bexternalencoderconfigwidget.ui new file mode 100644 index 0000000..dd32d08 --- /dev/null +++ b/plugins/encoder/external/base_k3bexternalencoderconfigwidget.ui @@ -0,0 +1,148 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>base_K3bExternalEncoderConfigWidget</class> +<author>Sebastian Trueg</author> +<widget class="QWidget"> + <property name="name"> + <cstring>base_K3bExternalEncoderConfigWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>441</width> + <height>354</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KActiveLabel"> + <property name="name"> + <cstring>kActiveLabel1</cstring> + </property> + <property name="text"> + <string><p>This dialog can be used to setup external command line applications as audio encoders. These can then be used by K3b to encode audio data (Tracks from an audio CD or the titles from an audio project) to formats that are normally not supported (i.e. no encoder plugin exists). +<p>K3b comes with a selection of predefined external applications that depends on the installed applications.</string> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Configured Encoders</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Extension</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Command</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_viewEncoders</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>71</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_buttonRemove</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_buttonEdit</cstring> + </property> + <property name="text"> + <string>Edit...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_buttonAdd</cstring> + </property> + <property name="text"> + <string>Add...</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kactivelabel.h</includehint> +</includehints> +</UI> diff --git a/plugins/encoder/external/base_k3bexternalencodereditwidget.ui b/plugins/encoder/external/base_k3bexternalencodereditwidget.ui new file mode 100644 index 0000000..9f3fb5a --- /dev/null +++ b/plugins/encoder/external/base_k3bexternalencodereditwidget.ui @@ -0,0 +1,164 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>base_K3bExternalEncoderEditWidget</class> +<author>Sebastian Trueg</author> +<widget class="QWidget"> + <property name="name"> + <cstring>base_K3bExternalEncoderEditWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>529</width> + <height>514</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>General</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_editExtension</cstring> + </property> + </widget> + <widget class="KLineEdit" row="1" column="0"> + <property name="name"> + <cstring>m_editName</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Filename extension:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Command</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KActiveLabel"> + <property name="name"> + <cstring>kActiveLabel2</cstring> + </property> + <property name="text"> + <string><p><b>Command</b><br> +Please insert the command used to encode the audio data. The command has to read raw little endian (see <em>Swap Byte Order</em>) 16bit stereo audio frames from stdin. +<p>The following strings will be replaced by K3b:<br> +<b>%f</b> - The filename of the resulting file. This is where the command has to write its output to.<br> +<em>The following refer to metadata stored for example in the ID3 tag of am mp3 file (Be aware that these values might be empty).</em><br> +<b>%t</b> - Title<br> +<b>%a</b> - Artist<br> +<b>%c</b> - Comment<br> +<b>%n</b> - Track number<br> +<b>%m</b> - Album Title<br> +<b>%r</b> - Album Artist<br> +<b>%x</b> - Album comment<br> +<b>%y</b> - Release Year</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>m_editCommand</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="title"> + <string>Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkSwapByteOrder</cstring> + </property> + <property name="text"> + <string>Swap &Byte Order</string> + </property> + <property name="toolTip" stdset="0"> + <string>Swap the byte order of the input data</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p> If this option is checked K3b will swap the byte order of the input data. Thus, the command has to read big endian audio frames. +<p>If the resulting audio file sounds bad it is highly likely that the byte order is wrong and this option has to be checked.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkWriteWaveHeader</cstring> + </property> + <property name="text"> + <string>Write W&ave Header</string> + </property> + <property name="toolTip" stdset="0"> + <string>Create a wave header for the input data</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>If this option is checked K3b will write a wave header. This is useful in case the encoder application cannot read plain raw audio data.</string> + </property> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>m_editName</tabstop> + <tabstop>m_editExtension</tabstop> + <tabstop>m_editCommand</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kactivelabel.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/plugins/encoder/external/k3bexternalencoder.cpp b/plugins/encoder/external/k3bexternalencoder.cpp new file mode 100644 index 0000000..4eb0218 --- /dev/null +++ b/plugins/encoder/external/k3bexternalencoder.cpp @@ -0,0 +1,380 @@ +/* + * + * $Id: k3bexternalencoder.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bexternalencoder.h" +#include "k3bexternalencodercommand.h" +#include "k3bexternalencoderconfigwidget.h" + +#include <k3bpluginfactory.h> +#include <k3bprocess.h> +#include <k3bcore.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include <qregexp.h> +#include <qfile.h> + +#include <sys/types.h> +#include <sys/wait.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3bexternalencoder, K3bPluginFactory<K3bExternalEncoder>( "libk3bexternalencoder" ) ) + + +static const char s_riffHeader[] = +{ + 0x52, 0x49, 0x46, 0x46, // 0 "RIFF" + 0x00, 0x00, 0x00, 0x00, // 4 wavSize + 0x57, 0x41, 0x56, 0x45, // 8 "WAVE" + 0x66, 0x6d, 0x74, 0x20, // 12 "fmt " + 0x10, 0x00, 0x00, 0x00, // 16 + 0x01, 0x00, 0x02, 0x00, // 20 + 0x44, 0xac, 0x00, 0x00, // 24 + 0x10, 0xb1, 0x02, 0x00, // 28 + 0x04, 0x00, 0x10, 0x00, // 32 + 0x64, 0x61, 0x74, 0x61, // 36 "data" + 0x00, 0x00, 0x00, 0x00 // 40 byteCount +}; + + + + + +static K3bExternalEncoderCommand commandByExtension( const QString& extension ) +{ + QValueList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() ); + for( QValueList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); it != cmds.end(); ++it ) + if( (*it).extension == extension ) + return *it; + + kdDebug() << "(K3bExternalEncoder) could not find command for extension " << extension << endl; + + return K3bExternalEncoderCommand(); +} + + +class K3bExternalEncoder::Private +{ +public: + Private() + : process(0) { + } + + K3bProcess* process; + QString fileName; + QString extension; + K3b::Msf length; + + K3bExternalEncoderCommand cmd; + + bool initialized; + + // the metaData we support + QString artist; + QString title; + QString comment; + QString trackNumber; + QString cdArtist; + QString cdTitle; + QString cdComment; + QString year; + QString genre; +}; + + +K3bExternalEncoder::K3bExternalEncoder( QObject* parent, const char* name ) + : K3bAudioEncoder( parent, name ) +{ + d = new Private(); +} + + +K3bExternalEncoder::~K3bExternalEncoder() +{ + delete d->process; + delete d; +} + + +void K3bExternalEncoder::setMetaDataInternal( K3bAudioEncoder::MetaDataField f, const QString& value ) +{ + switch( f ) { + case META_TRACK_TITLE: + d->title = value; + break; + case META_TRACK_ARTIST: + d->artist = value; + break; + case META_TRACK_COMMENT: + d->comment = value; + break; + case META_TRACK_NUMBER: + d->trackNumber = value; + break; + case META_ALBUM_TITLE: + d->cdTitle = value; + break; + case META_ALBUM_ARTIST: + d->cdArtist = value; + break; + case META_ALBUM_COMMENT: + d->cdComment = value; + break; + case META_YEAR: + d->year = value; + break; + case META_GENRE: + d->genre = value; + break; + } +} + + +void K3bExternalEncoder::finishEncoderInternal() +{ + if( d->process ) { + if( d->process->isRunning() ) { + ::close( d->process->stdinFd() ); + + // this is kind of evil... + // but we need to be sure the process exited when this method returnes + ::waitpid( d->process->pid(), 0, 0 ); + } + } +} + + +void K3bExternalEncoder::slotExternalProgramFinished( KProcess* p ) +{ + if( !p->normalExit() || p->exitStatus() != 0 ) + kdDebug() << "(K3bExternalEncoder) program exited with error." << endl; +} + + +bool K3bExternalEncoder::openFile( const QString& ext, const QString& filename, const K3b::Msf& length ) +{ + d->fileName = filename; + d->extension = ext; + d->initialized = false; + d->length = length; + + // delete existing files as some programs (like flac for example) might refuse to overwrite files + if( QFile::exists( filename ) ) + QFile::remove( filename ); + + return true; +} + + +void K3bExternalEncoder::closeFile() +{ + finishEncoderInternal(); +} + + +bool K3bExternalEncoder::initEncoderInternal( const QString& extension ) +{ + d->initialized = true; + + // find the correct command + d->cmd = commandByExtension( extension ); + + if( d->cmd.command.isEmpty() ) { + setLastError( i18n("Invalid command: the command is empty.") ); + return false; + } + + // setup the process + delete d->process; + d->process = new K3bProcess(); + d->process->setSplitStdout(true); + d->process->setRawStdin(true); + + connect( d->process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotExternalProgramFinished(KProcess*)) ); + connect( d->process, SIGNAL(stderrLine(const QString&)), + this, SLOT(slotExternalProgramOutputLine(const QString&)) ); + connect( d->process, SIGNAL(stdoutLine(const QString&)), + this, SLOT(slotExternalProgramOutputLine(const QString&)) ); + + + // create the commandline + QStringList params = QStringList::split( ' ', d->cmd.command, false ); + for( QStringList::iterator it = params.begin(); it != params.end(); ++it ) { + (*it).replace( "%f", d->fileName ); + (*it).replace( "%a", d->artist ); + (*it).replace( "%t", d->title ); + (*it).replace( "%c", d->comment ); + (*it).replace( "%y", d->year ); + (*it).replace( "%m", d->cdTitle ); + (*it).replace( "%r", d->cdArtist ); + (*it).replace( "%x", d->cdComment ); + (*it).replace( "%n", d->trackNumber ); + (*it).replace( "%g", d->genre ); + + *d->process << *it; + } + + + kdDebug() << "***** external parameters:" << endl; + const QValueList<QCString>& args = d->process->args(); + QString s; + for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << flush << endl; + + // set one general error message + setLastError( i18n("Command failed: %1").arg( s ) ); + + if( d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) { + if( d->cmd.writeWaveHeader ) + return writeWaveHeader(); + else + return true; + } + else { + QString commandName = d->cmd.command.section( QRegExp("\\s+"), 0 ); + if( !KStandardDirs::findExe( commandName ).isEmpty() ) + setLastError( i18n("Could not find program '%1'").arg(commandName) ); + + return false; + } +} + + +bool K3bExternalEncoder::writeWaveHeader() +{ + kdDebug() << "(K3bExternalEncoder) writing wave header" << endl; + + // write the RIFF thing + if( ::write( d->process->stdinFd(), s_riffHeader, 4 ) != 4 ) { + kdDebug() << "(K3bExternalEncoder) failed to write riff header." << endl; + return false; + } + + // write the wave size + Q_INT32 dataSize( d->length.audioBytes() ); + Q_INT32 wavSize( dataSize + 44 - 8 ); + char c[4]; + + c[0] = (wavSize >> 0 ) & 0xff; + c[1] = (wavSize >> 8 ) & 0xff; + c[2] = (wavSize >> 16) & 0xff; + c[3] = (wavSize >> 24) & 0xff; + + if( ::write( d->process->stdinFd(), c, 4 ) != 4 ) { + kdDebug() << "(K3bExternalEncoder) failed to write wave size." << endl; + return false; + } + + // write static part of the header + if( ::write( d->process->stdinFd(), s_riffHeader+8, 32 ) != 32 ) { + kdDebug() << "(K3bExternalEncoder) failed to write wave header." << endl; + return false; + } + + c[0] = (dataSize >> 0 ) & 0xff; + c[1] = (dataSize >> 8 ) & 0xff; + c[2] = (dataSize >> 16) & 0xff; + c[3] = (dataSize >> 24) & 0xff; + + if( ::write( d->process->stdinFd(), c, 4 ) != 4 ) { + kdDebug() << "(K3bExternalEncoder) failed to write data size." << endl; + return false; + } + + return true; +} + + +long K3bExternalEncoder::encodeInternal( const char* data, Q_ULONG len ) +{ + if( !d->initialized ) + if( !initEncoderInternal( d->extension ) ) + return -1; + + if( d->process ) { + if( d->process->isRunning() ) { + + long written = 0; + + // + // we swap the bytes to reduce user irritation ;) + // This is a little confused: We used to swap the byte order + // in older versions of this encoder since little endian seems + // to "feel" more natural. + // So now that we have a swap option we have to invert it to ensure + // compatibility + // + if( !d->cmd.swapByteOrder ) { + char* buffer = new char[len]; + for( unsigned int i = 0; i < len-1; i+=2 ) { + buffer[i] = data[i+1]; + buffer[i+1] = data[i]; + } + + written = ::write( d->process->stdinFd(), (const void*)buffer, len ); + delete [] buffer; + } + else + written = ::write( d->process->stdinFd(), (const void*)data, len ); + + return written; + } + else + return -1; + } + else + return -1; +} + + +void K3bExternalEncoder::slotExternalProgramOutputLine( const QString& line ) +{ + kdDebug() << "(" << d->cmd.name << ") " << line << endl; +} + + +QStringList K3bExternalEncoder::extensions() const +{ + QStringList el; + QValueList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() ); + for( QValueList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); it != cmds.end(); ++it ) + el.append( (*it).extension ); + + return el; +} + + +QString K3bExternalEncoder::fileTypeComment( const QString& ext ) const +{ + return commandByExtension( ext ).name; +} + + +K3bPluginConfigWidget* K3bExternalEncoder::createConfigWidget( QWidget* parent, + const char* name ) const +{ + return new K3bExternalEncoderSettingsWidget( parent, name ); +} + + +#include "k3bexternalencoder.moc" diff --git a/plugins/encoder/external/k3bexternalencoder.h b/plugins/encoder/external/k3bexternalencoder.h new file mode 100644 index 0000000..1612927 --- /dev/null +++ b/plugins/encoder/external/k3bexternalencoder.h @@ -0,0 +1,68 @@ +/* + * + * $Id: k3bexternalencoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_EXTERNAL_ENCODER_H_ +#define _K3B_EXTERNAL_ENCODER_H_ + + +#include <k3baudioencoder.h> + + +class base_K3bExternalEncoderConfigWidget; +class KProcess; + + +class K3bExternalEncoder : public K3bAudioEncoder +{ + Q_OBJECT + + public: + K3bExternalEncoder( QObject* parent = 0, const char* name = 0 ); + ~K3bExternalEncoder(); + + QStringList extensions() const; + + QString fileTypeComment( const QString& ) const; + + int pluginSystemVersion() const { return 3; } + + K3bPluginConfigWidget* createConfigWidget( QWidget* parent, + const char* name ) const; + + /** + * reimplemented since the external program is intended to write the file + * TODO: allow writing to stdout. + */ + bool openFile( const QString& ext, const QString& filename, const K3b::Msf& length ); + void closeFile(); + + class Command; + + private slots: + void slotExternalProgramFinished( KProcess* ); + void slotExternalProgramOutputLine( const QString& ); + + private: + void finishEncoderInternal(); + bool initEncoderInternal( const QString& extension ); + long encodeInternal( const char* data, Q_ULONG len ); + void setMetaDataInternal( MetaDataField, const QString& ); + bool writeWaveHeader(); + + class Private; + Private* d; +}; + +#endif diff --git a/plugins/encoder/external/k3bexternalencoder.plugin b/plugins/encoder/external/k3bexternalencoder.plugin new file mode 100644 index 0000000..2faaa3a --- /dev/null +++ b/plugins/encoder/external/k3bexternalencoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bexternalencoder +Group=AudioEncoder +Name=K3b External Audio Encoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=4.0 +Comment=Encoding module that allows specifying an encoding commmand +License=GPL diff --git a/plugins/encoder/external/k3bexternalencodercommand.cpp b/plugins/encoder/external/k3bexternalencodercommand.cpp new file mode 100644 index 0000000..5dcfb23 --- /dev/null +++ b/plugins/encoder/external/k3bexternalencodercommand.cpp @@ -0,0 +1,110 @@ +/* + * + * $Id: k3bexternalencoder.cpp 567280 2006-07-28 13:26:27Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bexternalencodercommand.h" + +#include <k3bcore.h> + +#include <kconfig.h> +#include <kstandarddirs.h> + + +QValueList<K3bExternalEncoderCommand> K3bExternalEncoderCommand::readCommands() +{ + KConfig* c = k3bcore->config(); + + c->setGroup( "K3bExternalEncoderPlugin" ); + + QValueList<K3bExternalEncoderCommand> cl; + + QStringList cmds = c->readListEntry( "commands" ); + for( QStringList::iterator it = cmds.begin(); it != cmds.end(); ++it ) { + QStringList cmdString = c->readListEntry( "command_" + *it ); + K3bExternalEncoderCommand cmd; + cmd.name = cmdString[0]; + cmd.extension = cmdString[1]; + cmd.command = cmdString[2]; + for( unsigned int i = 3; i < cmdString.count(); ++i ) { + if( cmdString[i] == "swap" ) + cmd.swapByteOrder = true; + else if( cmdString[i] == "wave" ) + cmd.writeWaveHeader = true; + } + cl.append(cmd); + } + + // some defaults + if( cmds.isEmpty() ) { + // check if the lame encoding plugin has been compiled +#ifndef HAVE_LAME + K3bExternalEncoderCommand lameCmd; + lameCmd.name = "Mp3 (Lame)"; + lameCmd.extension = "mp3"; + lameCmd.command = "lame -h --tt %t --ta %a --tl %m --ty %y --tc %c - %f"; + + cl.append( lameCmd ); +#endif + + if( !KStandardDirs::findExe( "flac" ).isEmpty() ) { + K3bExternalEncoderCommand flacCmd; + flacCmd.name = "Flac"; + flacCmd.extension = "flac"; + flacCmd.command = "flac " + "-V " + "-o %f " + "--force-raw-format " + "--endian=big " + "--channels=2 " + "--sample-rate=44100 " + "--sign=signed " + "--bps=16 " + "-T ARTIST=%a " + "-T TITLE=%t " + "-T TRACKNUMBER=%n " + "-T DATE=%y " + "-T ALBUM=%m " + "-"; + + cl.append( flacCmd ); + } + + if( !KStandardDirs::findExe( "mppenc" ).isEmpty() ) { + K3bExternalEncoderCommand mppCmd; + mppCmd.name = "Musepack"; + mppCmd.extension = "mpc"; + mppCmd.command = "mppenc " + "--standard " + "--overwrite " + "--silent " + "--artist %a " + "--title %t " + "--track %n " + "--album %m " + "--comment %c " + "--year %y " + "- " + "%f"; + mppCmd.swapByteOrder = true; + mppCmd.writeWaveHeader = true; + + cl.append( mppCmd ); + } + } + + return cl; +} + diff --git a/plugins/encoder/external/k3bexternalencodercommand.h b/plugins/encoder/external/k3bexternalencodercommand.h new file mode 100644 index 0000000..3351191 --- /dev/null +++ b/plugins/encoder/external/k3bexternalencodercommand.h @@ -0,0 +1,40 @@ +/* + * + * $Id: k3bexternalencoder.cpp 567280 2006-07-28 13:26:27Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_EXTERNAL_ENCODER_COMMAND_H_ +#define _K3B_EXTERNAL_ENCODER_COMMAND_H_ + +#include <qstring.h> +#include <qvaluelist.h> + +class K3bExternalEncoderCommand +{ +public: + K3bExternalEncoderCommand() + : swapByteOrder(false), + writeWaveHeader(false) { + } + + QString name; + QString extension; + QString command; + + bool swapByteOrder; + bool writeWaveHeader; + + static QValueList<K3bExternalEncoderCommand> readCommands(); +}; + +#endif diff --git a/plugins/encoder/external/k3bexternalencoderconfigwidget.cpp b/plugins/encoder/external/k3bexternalencoderconfigwidget.cpp new file mode 100644 index 0000000..3ab59c7 --- /dev/null +++ b/plugins/encoder/external/k3bexternalencoderconfigwidget.cpp @@ -0,0 +1,228 @@ +/* + * + * $Id: k3bexternalencoder.cpp 567280 2006-07-28 13:26:27Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bexternalencoderconfigwidget.h" + +#include <k3bcore.h> + +#include <qcheckbox.h> +#include <qlayout.h> +#include <qlistview.h> +#include <qpushbutton.h> + +#include <klineedit.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <klocale.h> + + + + +K3bExternalEncoderEditDialog::K3bExternalEncoderEditDialog( QWidget* parent ) + : KDialogBase( Swallow, + i18n("Editing external audio encoder"), + Ok|Cancel, + Ok, + parent ) +{ + m_editW = new base_K3bExternalEncoderEditWidget( this ); + setMainWidget( m_editW ); +} + + +K3bExternalEncoderEditDialog::~K3bExternalEncoderEditDialog() +{ +} + + +K3bExternalEncoderCommand K3bExternalEncoderEditDialog::currentCommand() const +{ + K3bExternalEncoderCommand cmd; + cmd.name = m_editW->m_editName->text(); + cmd.extension = m_editW->m_editExtension->text(); + cmd.command = m_editW->m_editCommand->text(); + cmd.swapByteOrder = m_editW->m_checkSwapByteOrder->isChecked(); + cmd.writeWaveHeader = m_editW->m_checkWriteWaveHeader->isChecked(); + return cmd; +} + + +void K3bExternalEncoderEditDialog::setCommand( const K3bExternalEncoderCommand& cmd ) +{ + m_editW->m_editName->setText( cmd.name ); + m_editW->m_editExtension->setText( cmd.extension ); + m_editW->m_editCommand->setText( cmd.command ); + m_editW->m_checkSwapByteOrder->setChecked( cmd.swapByteOrder ); + m_editW->m_checkWriteWaveHeader->setChecked( cmd.writeWaveHeader ); +} + + +void K3bExternalEncoderEditDialog::slotOk() +{ + if( m_editW->m_editName->text().isEmpty() ) { + KMessageBox::error( this, + i18n("Please specify a name for the command."), + i18n("No name specified") ); + } + else if( m_editW->m_editExtension->text().isEmpty() ) { + KMessageBox::error( this, + i18n("Please specify an extension for the command."), + i18n("No extension specified") ); + } + else if( m_editW->m_editCommand->text().isEmpty() ) { + KMessageBox::error( this, + i18n("Please specify the command line."), + i18n("No command line specified") ); + } + else if( !m_editW->m_editCommand->text().contains( "%f" ) ) { + KMessageBox::error( this, + i18n("Please add the output filename (%f) to the command line."), + i18n("No filename specified") ); + } + // FIXME: check for name and extension uniqueness + else { + KDialogBase::slotOk(); + } +} + + + + + + +class K3bExternalEncoderSettingsWidget::Private +{ +public: + QMap<QListViewItem*, K3bExternalEncoderCommand> commands; +}; + + +K3bExternalEncoderSettingsWidget::K3bExternalEncoderSettingsWidget( QWidget* parent, const char* name ) + : K3bPluginConfigWidget( parent, name ) +{ + d = new Private(); + + w = new base_K3bExternalEncoderConfigWidget( this ); + QHBoxLayout* lay = new QHBoxLayout( this ); + lay->setMargin( 0 ); + lay->addWidget( w ); + + connect( w->m_viewEncoders, SIGNAL(selectionChanged()), + this, SLOT(slotSelectionChanged()) ); + connect( w->m_buttonAdd, SIGNAL(clicked()), + this, SLOT(slotNewCommand()) ); + connect( w->m_buttonEdit, SIGNAL(clicked()), + this, SLOT(slotEditCommand()) ); + connect( w->m_buttonRemove, SIGNAL(clicked()), + this, SLOT(slotRemoveCommand()) ); + + m_editDlg = new K3bExternalEncoderEditDialog( this ); +} + + +K3bExternalEncoderSettingsWidget::~K3bExternalEncoderSettingsWidget() +{ + delete d; +} + + +void K3bExternalEncoderSettingsWidget::slotNewCommand() +{ + // clear the dialog + m_editDlg->setCommand( K3bExternalEncoderCommand() ); + + if( m_editDlg->exec() == QDialog::Accepted ) { + K3bExternalEncoderCommand cmd = m_editDlg->currentCommand(); + d->commands.insert( new QListViewItem( w->m_viewEncoders, + w->m_viewEncoders->lastItem(), + cmd.name, + cmd.extension, + cmd.command ), + cmd ); + } +} + + +void K3bExternalEncoderSettingsWidget::slotSelectionChanged() +{ + w->m_buttonRemove->setEnabled( w->m_viewEncoders->selectedItem() != 0 ); + w->m_buttonEdit->setEnabled( w->m_viewEncoders->selectedItem() != 0 ); +} + + +void K3bExternalEncoderSettingsWidget::slotEditCommand() +{ + if( QListViewItem* item = w->m_viewEncoders->selectedItem() ) { + m_editDlg->setCommand( d->commands[item] ); + if( m_editDlg->exec() == QDialog::Accepted ) { + d->commands[item] = m_editDlg->currentCommand(); + } + } +} + + +void K3bExternalEncoderSettingsWidget::slotRemoveCommand() +{ + if( QListViewItem* item = w->m_viewEncoders->selectedItem() ) { + d->commands.erase( item ); + delete item; + } +} + + +void K3bExternalEncoderSettingsWidget::loadConfig() +{ + d->commands.clear(); + w->m_viewEncoders->clear(); + + QValueList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() ); + for( QValueList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); + it != cmds.end(); ++it ) { + K3bExternalEncoderCommand& cmd = *it; + d->commands.insert( new QListViewItem( w->m_viewEncoders, + w->m_viewEncoders->lastItem(), + cmd.name, + cmd.extension, + cmd.command ), + cmd ); + } +} + + +void K3bExternalEncoderSettingsWidget::saveConfig() +{ + KConfig* c = k3bcore->config(); + c->deleteGroup( "K3bExternalEncoderPlugin", true ); + c->setGroup( "K3bExternalEncoderPlugin" ); + + QStringList cmdNames; + for( QMapIterator<QListViewItem*, K3bExternalEncoderCommand> it = d->commands.begin(); + it != d->commands.end(); ++it ) { + QStringList cmd; + cmd << it.data().name << it.data().extension << it.data().command; + if( it.data().swapByteOrder ) + cmd << "swap"; + if( it.data().writeWaveHeader ) + cmd << "wave"; + c->writeEntry( "command_" + it.data().name, cmd ); + cmdNames << it.data().name; + } + c->writeEntry( "commands", cmdNames ); +} + + + + +#include "k3bexternalencoderconfigwidget.moc" diff --git a/plugins/encoder/external/k3bexternalencoderconfigwidget.h b/plugins/encoder/external/k3bexternalencoderconfigwidget.h new file mode 100644 index 0000000..4f6dd47 --- /dev/null +++ b/plugins/encoder/external/k3bexternalencoderconfigwidget.h @@ -0,0 +1,72 @@ +/* + * + * $Id: k3bexternalencoder.cpp 567280 2006-07-28 13:26:27Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_EXTERNAL_ENCODER_CONFIG_WIDGET_H_ +#define _K3B_EXTERNAL_ENCODER_CONFIG_WIDGET_H_ + +#include "base_k3bexternalencodereditwidget.h" +#include "base_k3bexternalencoderconfigwidget.h" +#include "k3bexternalencodercommand.h" + +#include <k3bpluginconfigwidget.h> +#include <kdialogbase.h> + + +class K3bExternalEncoderEditDialog : public KDialogBase +{ + Q_OBJECT + + public: + K3bExternalEncoderEditDialog( QWidget* parent ); + ~K3bExternalEncoderEditDialog(); + + K3bExternalEncoderCommand currentCommand() const; + void setCommand( const K3bExternalEncoderCommand& cmd ); + + private slots: + void slotOk(); + + private: + base_K3bExternalEncoderEditWidget* m_editW; +}; + + +class K3bExternalEncoderSettingsWidget : public K3bPluginConfigWidget +{ + Q_OBJECT + + public: + K3bExternalEncoderSettingsWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bExternalEncoderSettingsWidget(); + + public slots: + void loadConfig(); + void saveConfig(); + + private slots: + void slotSelectionChanged(); + void slotNewCommand(); + void slotEditCommand(); + void slotRemoveCommand(); + + private: + base_K3bExternalEncoderConfigWidget* w; + K3bExternalEncoderEditDialog* m_editDlg; + + class Private; + Private* d; +}; + +#endif diff --git a/plugins/encoder/lame/Makefile.am b/plugins/encoder/lame/Makefile.am new file mode 100644 index 0000000..4a4d61e --- /dev/null +++ b/plugins/encoder/lame/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3blameencoder.la + +libk3blameencoder_la_SOURCES = base_k3blameencodersettingswidget.ui \ + base_k3bmanualbitratesettingswidget.ui \ + k3blameencoder.cpp + +libk3blameencoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDECORE) -lmp3lame +libk3blameencoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3blameencoder.plugin + +METASOURCES = AUTO diff --git a/plugins/encoder/lame/base_k3blameencodersettingswidget.ui b/plugins/encoder/lame/base_k3blameencodersettingswidget.ui new file mode 100644 index 0000000..199895e --- /dev/null +++ b/plugins/encoder/lame/base_k3blameencodersettingswidget.ui @@ -0,0 +1,480 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>base_K3bLameEncoderSettingsWidget</class> +<author>Sebastian Trueg</author> +<widget class="QWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>337</width> + <height>295</height> + </rect> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>m_mainTab</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Settings</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>m_groupVariableBitrate</cstring> + </property> + <property name="title"> + <string>Quality Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_radioQualityLevel</cstring> + </property> + <property name="text"> + <string>Preset:</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_labelQualityLevel</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>textLabel1</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + </hbox> + </widget> + <widget class="QFrame"> + <property name="name"> + <cstring>m_qualityLevelFrame</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string>high quality</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string>small file</string> + </property> + </widget> + <widget class="QSlider" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_sliderQuality</cstring> + </property> + <property name="maxValue"> + <number>9</number> + </property> + <property name="pageStep"> + <number>3</number> + </property> + <property name="value"> + <number>3</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_radioManual</cstring> + </property> + <property name="text"> + <string>Manual settings:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_labelManualSettings</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>textLabel2</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <spacer> + <property name="name"> + <cstring>spacer2_2_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_buttonManualSettings</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Change Settings...</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Advanced</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Encoder Quality</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="KIntNumInput"> + <property name="name"> + <cstring>m_spinEncoderQuality</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Choose the noise shaping & psycho acoustic algorithm.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Bitrate is of course the main influence on quality. The higher the bitrate, the higher the quality. But for a given bitrate, we have a choice of algorithms to determine the best scalefactors and huffman encoding (noise shaping). +<p>The quality increases from 0 to 9 while the encoding speed drops. +<p>9 uses the slowest & best possible version of all algorithms. +<p><b>7 is the recommended setting</b> while 4 still produced reasonable quality at good speed. +<p>0 disables almost all algorithms including psy-model resulting in poor quality. +<p><b>This setting has no influence on the size of the resulting file.</b></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string>fast encoding</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string>high quality</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkCopyright</cstring> + </property> + <property name="text"> + <string>Mark copyrighted</string> + </property> + <property name="toolTip" stdset="0"> + <string>Mark the encoded file as being copyrighted.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkOriginal</cstring> + </property> + <property name="text"> + <string>Mark as original</string> + </property> + <property name="toolTip" stdset="0"> + <string>Mark the encoded file as being a copy.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkISO</cstring> + </property> + <property name="text"> + <string>Strict ISO compliance</string> + </property> + <property name="toolTip" stdset="0"> + <string>Enforce strict ISO compliance</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>If this option is checked LAME will enforce the 7680 bit limitation on total frame size.<br> +This results in many wasted bits for high bitrate encodings but will ensure strict ISO compatibility. This compatibility might be important for hardware players.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkError</cstring> + </property> + <property name="text"> + <string>Error protection</string> + </property> + <property name="toolTip" stdset="0"> + <string>Turn on CRC error protection.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>If this option is checked a cyclic redundancy check (CRC) code will be added to each frame, allowing transmission errors that could occur on the MP3 stream to be detected; however, it takes 16 bits per frame that would otherwise be used for encoding, thus slightly reducing the sound quality.</string> + </property> + </widget> + </vbox> + </widget> + </vbox> + </widget> + </widget> + </hbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>m_radioManual</sender> + <signal>toggled(bool)</signal> + <receiver>m_buttonManualSettings</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_radioQualityLevel</sender> + <signal>toggled(bool)</signal> + <receiver>m_qualityLevelFrame</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_radioQualityLevel</sender> + <signal>toggled(bool)</signal> + <receiver>m_labelQualityLevel</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_radioManual</sender> + <signal>toggled(bool)</signal> + <receiver>m_labelManualSettings</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/plugins/encoder/lame/base_k3bmanualbitratesettingswidget.ui b/plugins/encoder/lame/base_k3bmanualbitratesettingswidget.ui new file mode 100644 index 0000000..a3bff90 --- /dev/null +++ b/plugins/encoder/lame/base_k3bmanualbitratesettingswidget.ui @@ -0,0 +1,273 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>base_K3bManualBitrateSettingsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>base_K3bManualBitrateSettingsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>283</width> + <height>298</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="title"> + <string>Quality</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_radioConstantBitrate</cstring> + </property> + <property name="text"> + <string>Constant Bitrate</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox"> + <property name="name"> + <cstring>m_comboConstantBitrate</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_radioVariableBitrate</cstring> + </property> + <property name="text"> + <string>Variable Bitrate</string> + </property> + </widget> + <widget class="QFrame"> + <property name="name"> + <cstring>frame5</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KComboBox" row="2" column="2"> + <property name="name"> + <cstring>m_comboMinimumBitrate</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <spacer row="0" column="0" rowspan="3" colspan="1"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="0" column="2"> + <property name="name"> + <cstring>m_comboMaximumBitrate</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>m_checkBitrateMaximum</cstring> + </property> + <property name="text"> + <string>Maximum bitrate:</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="1"> + <property name="name"> + <cstring>m_checkBitrateMinimum</cstring> + </property> + <property name="text"> + <string>Minimum bitrate:</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="1"> + <property name="name"> + <cstring>m_checkBitrateAverage</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Average bitrate:</string> + </property> + </widget> + <widget class="QSpinBox" row="1" column="2"> + <property name="name"> + <cstring>m_spinAverageBitrate</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> kbps</string> + </property> + <property name="buttonSymbols"> + <enum>UpDownArrows</enum> + </property> + <property name="maxValue"> + <number>310</number> + </property> + <property name="minValue"> + <number>8</number> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Channel Mode</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox"> + <item> + <property name="text"> + <string>Stereo</string> + </property> + </item> + <item> + <property name="text"> + <string>Joint Stereo</string> + </property> + </item> + <item> + <property name="text"> + <string>Mono</string> + </property> + </item> + <property name="name"> + <cstring>m_comboMode</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Select the channel mode.</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Select the channel mode of the resulting Mp3 file: +<p><b>Stereo</b><br> +In this mode, the encoder makes no use of potential correlations between the two input channels; it can, however, negotiate the bit demand between both channel, i.e. give one channel more bits if the other contains silence or needs fewer bits because of a lower complexity. +<p><b>Joint-Stereo</b><br> +In this mode, the encoder will make use of correlations between both channels. The signal will be matrixed into a sum ("mid"), computed by L+R, and difference ("side") signal, computed by L-R, and more bits are allocated to the mid channel. This will effectively increase the bandwidth if the signal does not have too much stereo separation, thus giving a significant gain in encoding quality. +<p><b>Mono</b><br> +The input will be encoded as a mono signal. If it was a stereo signal, it will be downsampled to mono. The downmix is calculated as the sum of the left and right channel, attenuated by 6 dB.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>m_radioVariableBitrate</sender> + <signal>toggled(bool)</signal> + <receiver>frame5</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_radioConstantBitrate</sender> + <signal>toggled(bool)</signal> + <receiver>m_comboConstantBitrate</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkBitrateMaximum</sender> + <signal>toggled(bool)</signal> + <receiver>m_comboMaximumBitrate</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkBitrateAverage</sender> + <signal>toggled(bool)</signal> + <receiver>m_spinAverageBitrate</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkBitrateMinimum</sender> + <signal>toggled(bool)</signal> + <receiver>m_comboMinimumBitrate</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/plugins/encoder/lame/configure.in.bot b/plugins/encoder/lame/configure.in.bot new file mode 100644 index 0000000..5bb35c9 --- /dev/null +++ b/plugins/encoder/lame/configure.in.bot @@ -0,0 +1,11 @@ +echo "" + +if test x$have_lame = xyes; then + echo "K3b - Lame Mp3 encoder plugin: yes" +else + echo "K3b - Lame Mp3 encoder plugin no" +if test "$ac_cv_use_lame" = "yes"; then + echo "K3b - You are missing the Lame headers and libraries." + echo "K3b - The Lame Mp3 encoding plugin won't be compiled." +fi +fi diff --git a/plugins/encoder/lame/configure.in.in b/plugins/encoder/lame/configure.in.in new file mode 100644 index 0000000..f2a5283 --- /dev/null +++ b/plugins/encoder/lame/configure.in.in @@ -0,0 +1,20 @@ +dnl === test for LAME - begin ==== +AC_ARG_WITH( + lame, + AS_HELP_STRING([--without-lame], [build K3b without LAME support (default=no)]), + [ac_cv_use_lame=$withval], + [ac_cv_use_lame=yes] +) + +have_lame=no +if test "$ac_cv_use_lame" = "yes"; then + KDE_CHECK_HEADERS(lame/lame.h, [ + AC_CHECK_LIB(mp3lame, lame_init, [ + have_lame=yes + AC_DEFINE(HAVE_LAME,1,[defined if you have the lame header and lib]) + ], [], $all_libraries -lm) + ]) +fi + +AM_CONDITIONAL(include_LAME, [test x$have_lame = xyes]) +dnl === test for LAME - end ==== diff --git a/plugins/encoder/lame/k3blameencoder.cpp b/plugins/encoder/lame/k3blameencoder.cpp new file mode 100644 index 0000000..089c7d0 --- /dev/null +++ b/plugins/encoder/lame/k3blameencoder.cpp @@ -0,0 +1,627 @@ +/* + * + * $Id: k3blameencoder.cpp 653779 2007-04-14 07:58:51Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3blameencoder.h" + +#include <k3bcore.h> +#include <k3bpluginfactory.h> + +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kinstance.h> +#include <knuminput.h> +#include <kcombobox.h> +#include <kdialogbase.h> + +#include <qlayout.h> +#include <qcstring.h> +#include <qradiobutton.h> +#include <qcheckbox.h> +#include <qspinbox.h> +#include <qgroupbox.h> +#include <qbuttongroup.h> +#include <qtextcodec.h> +#include <qfile.h> +#include <qslider.h> +#include <qlabel.h> +#include <qpushbutton.h> + +#include <stdio.h> +#include <lame/lame.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3blameencoder, K3bPluginFactory<K3bLameEncoder>( "libk3blameencoder" ) ) + + +static const int s_lame_bitrates[] = { + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 0 // just used for the loops below +}; + + +static const int s_lame_presets[] = { + 56, // ABR for Voice, Radio, Mono, etc. + 90, // + + V6, // ~115 kbps + V5, // ~130 kbps | Portable - small size + V4, // ~160 kbps + + V3, // ~175 kbps + V2, // ~190 kbps | HiFi - for home or quite listening + V1, // ~210 kbps | + V0, // ~230 kbps + + 320 // ABR 320 neary lossless for archiving (not recommended, use flac instead) +}; + + +static const int s_lame_preset_approx_bitrates[] = { + 56, + 90, + 115, + 130, + 160, + 175, + 190, + 210, + 230, + 320 +}; + + +static const char* s_lame_preset_strings[] = { + I18N_NOOP("Low quality (56 kbps)"), + I18N_NOOP("Low quality (90 kbps)"), + + I18N_NOOP("Portable (average 115 kbps)"), + I18N_NOOP("Portable (average 130 kbps)"), + I18N_NOOP("Portable (average 160 kbps)"), + + I18N_NOOP("HiFi (average 175 kbps)"), + I18N_NOOP("HiFi (average 190 kbps)"), + I18N_NOOP("HiFi (average 210 kbps)"), + I18N_NOOP("HiFi (average 230 kbps)"), + + I18N_NOOP("Archiving (320 kbps)"), +}; + + +static const char* s_lame_mode_strings[] = { + I18N_NOOP("Stereo"), + I18N_NOOP("Joint Stereo"), + I18N_NOOP("Mono") +}; + + + +class K3bLameEncoder::Private +{ +public: + Private() + : flags(0), + fid(0) { + } + + lame_global_flags* flags; + + char buffer[8000]; + + QString filename; + FILE* fid; +}; + + + + +K3bLameEncoder::K3bLameEncoder( QObject* parent, const char* name ) + : K3bAudioEncoder( parent, name ) +{ + d = new Private(); +} + + +K3bLameEncoder::~K3bLameEncoder() +{ + closeFile(); + + delete d; +} + + +bool K3bLameEncoder::openFile( const QString& extension, const QString& filename, const K3b::Msf& length ) +{ + closeFile(); + + d->filename = filename; + d->fid = ::fopen( QFile::encodeName( filename ), "w+" ); + if( d->fid ) + return initEncoder( extension, length ); + else + return false; +} + + +bool K3bLameEncoder::isOpen() const +{ + return ( d->fid != 0 ); +} + + +void K3bLameEncoder::closeFile() +{ + if( isOpen() ) { + finishEncoder(); + ::fclose( d->fid ); + d->fid = 0; + d->filename.truncate(0); + } +} + + +const QString& K3bLameEncoder::filename() const +{ + return d->filename; +} + + +bool K3bLameEncoder::initEncoderInternal( const QString&, const K3b::Msf& length ) +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3bLameEncoderPlugin" ); + + d->flags = lame_init(); + + if( d->flags == 0 ) { + kdDebug() << "(K3bLameEncoder) lame_init failed." << endl; + return false; + } + + // + // set the format of the input data + // + lame_set_num_samples( d->flags, length.lba()*588 ); + lame_set_in_samplerate( d->flags, 44100 ); + lame_set_num_channels( d->flags, 2 ); + + // + // Lame by default determines the samplerate based on the bitrate + // since we have no option for the user to influence this yet + // we just keep to the good old 44.1 khz + // + lame_set_out_samplerate( d->flags, 44100 ); + + // + // Choose the quality level + // + if( c->readBoolEntry( "Manual Bitrate Settings", false ) ) { + // + // Mode + // + QString mode = c->readEntry( "Mode", "stereo" ); + if( mode == "stereo" ) + lame_set_mode( d->flags, STEREO ); + else if( mode == "joint" ) + lame_set_mode( d->flags, JOINT_STEREO ); + else // mono + lame_set_mode( d->flags, MONO ); + + // + // Variable Bitrate + // + if( c->readBoolEntry( "VBR", false ) ) { + // we use the default algorithm here + lame_set_VBR( d->flags, vbr_default ); + + if( c->readBoolEntry( "Use Maximum Bitrate", false ) ) { + lame_set_VBR_max_bitrate_kbps( d->flags, c->readNumEntry( "Maximum Bitrate", 224 ) ); + } + if( c->readBoolEntry( "Use Minimum Bitrate", false ) ) { + lame_set_VBR_min_bitrate_kbps( d->flags, c->readNumEntry( "Minimum Bitrate", 32 ) ); + + // TODO: lame_set_hard_min + } + if( c->readBoolEntry( "Use Average Bitrate", true ) ) { + lame_set_VBR( d->flags, vbr_abr ); + lame_set_VBR_mean_bitrate_kbps( d->flags, c->readNumEntry( "Average Bitrate", 128 ) ); + } + } + + // + // Constant Bitrate + // + else { + lame_set_VBR( d->flags, vbr_off ); + lame_set_brate( d->flags, c->readNumEntry( "Constant Bitrate", 128 ) ); + } + } + + else { + // + // In lame 0 is the highest quality. Since that is just confusing for the user + // if we call the setting "Quality" we simply invert the value. + // + int q = c->readNumEntry( "Quality Level", 5 ); + if( q < 0 ) q = 0; + if( q > 9 ) q = 9; + + kdDebug() << "(K3bLameEncoder) setting preset encoding value to " << q << endl; + + if ( q < 2 || q > 8 ) { + lame_set_VBR( d->flags, vbr_abr ); + } + else { + lame_set_VBR( d->flags, vbr_default ); + } + lame_set_preset( d->flags, s_lame_presets[q] ); + + if( q < 2 ) + lame_set_mode( d->flags, MONO ); + } + + + // + // file options + // + lame_set_copyright( d->flags, c->readBoolEntry( "Copyright", false ) ); + lame_set_original( d->flags, c->readBoolEntry( "Original", true ) ); + lame_set_strict_ISO( d->flags, c->readBoolEntry( "ISO compliance", false ) ); + lame_set_error_protection( d->flags, c->readBoolEntry( "Error Protection", false ) ); + + + // + // Used Algorithm + // + // default to 2 which is the same as the -h lame option + // THIS HAS NO INFLUENCE ON THE SIZE OF THE FILE! + // + // + // In lame 0 is the highest quality. Since that is just confusing for the user + // if we call the setting "Quality" we simply invert the value. + // + int q = c->readNumEntry( "Encoder Quality", 7 ); + if( q < 0 ) q = 0; + if( q > 9 ) q = 9; + lame_set_quality( d->flags, 9-q ); + + // + // ID3 settings + // + // for now we default to both v1 and v2 tags + id3tag_add_v2( d->flags ); + id3tag_pad_v2( d->flags ); + + return( lame_init_params( d->flags ) != -1 ); +} + + +long K3bLameEncoder::encodeInternal( const char* data, Q_ULONG len ) +{ + // FIXME: we may have to swap data here + int size = lame_encode_buffer_interleaved( d->flags, + (short int*)data, + len/4, + (unsigned char*)d->buffer, + 8000 ); + if( size < 0 ) { + kdDebug() << "(K3bLameEncoder) lame_encode_buffer_interleaved failed." << endl; + return -1; + } + + return ::fwrite( d->buffer, 1, size, d->fid ); +} + + +void K3bLameEncoder::finishEncoderInternal() +{ + int size = lame_encode_flush( d->flags, + (unsigned char*)d->buffer, + 8000 ); + if( size > 0 ) + ::fwrite( d->buffer, 1, size, d->fid ); + + lame_mp3_tags_fid( d->flags, d->fid ); + + lame_close( d->flags ); + d->flags = 0; +} + + +void K3bLameEncoder::setMetaDataInternal( K3bAudioEncoder::MetaDataField f, const QString& value ) +{ + // let's not use UTF-8 here since I don't know how to tell lame... + // FIXME: when we use the codec we only get garbage. Why? + QTextCodec* codec = 0;//QTextCodec::codecForName( "ISO8859-1" ); +// if( !codec ) +// kdDebug() << "(K3bLameEncoder) could not find codec ISO8859-1." << endl; + + switch( f ) { + case META_TRACK_TITLE: + id3tag_set_title( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ); + break; + case META_TRACK_ARTIST: + id3tag_set_artist( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ); + break; + case META_ALBUM_TITLE: + id3tag_set_album( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ); + break; + case META_ALBUM_COMMENT: + id3tag_set_comment( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ); + break; + case META_YEAR: + id3tag_set_year( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ); + break; + case META_TRACK_NUMBER: + id3tag_set_track( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ); + break; + case META_GENRE: + if( id3tag_set_genre( d->flags, codec ? codec->fromUnicode(value).data() : value.latin1() ) ) + kdDebug() << "(K3bLameEncoder) unable to set genre." << endl; + break; + default: + return; + } + + if( lame_init_params( d->flags ) < 0 ) + kdDebug() << "(K3bLameEncoder) lame_init_params failed." << endl; +} + + + + + +K3bLameEncoderSettingsWidget::K3bLameEncoderSettingsWidget( QWidget* parent, const char* name ) + : K3bPluginConfigWidget( parent, name ) +{ + m_w = new base_K3bLameEncoderSettingsWidget( this ); + m_w->m_sliderQuality->setRange( 0, 9 ); + m_w->m_spinEncoderQuality->setRange( 0, 9, true ); + + m_manualSettingsDlg = new KDialogBase( this, 0, true, + i18n("(Lame) Manual Quality Settings") ); + m_brW = new base_K3bManualBitrateSettingsWidget( m_manualSettingsDlg ); + m_manualSettingsDlg->setMainWidget( m_brW ); + + for( int i = 0; s_lame_bitrates[i]; ++i ) + m_brW->m_comboMaximumBitrate->insertItem( i18n("%1 kbps" ).arg(s_lame_bitrates[i]) ); + + for( int i = 0; s_lame_bitrates[i]; ++i ) + m_brW->m_comboMinimumBitrate->insertItem( i18n("%1 kbps" ).arg(s_lame_bitrates[i]) ); + + for( int i = 0; s_lame_bitrates[i]; ++i ) + m_brW->m_comboConstantBitrate->insertItem( i18n("%1 kbps" ).arg(s_lame_bitrates[i]) ); + + + QHBoxLayout* lay = new QHBoxLayout( this ); + lay->setMargin( 0 ); + lay->addWidget( m_w ); + + // TODO: add whatsthis help for the quality level. + // QString qualityLevelWhatsThis = i18n("<p>"); + + connect( m_w->m_buttonManualSettings, SIGNAL(clicked()), + this, SLOT(slotShowManualSettings()) ); + connect( m_w->m_sliderQuality, SIGNAL(valueChanged(int)), + this, SLOT(slotQualityLevelChanged(int)) ); + + updateManualSettingsLabel(); + slotQualityLevelChanged( 5 ); +} + + +K3bLameEncoderSettingsWidget::~K3bLameEncoderSettingsWidget() +{ +} + + +void K3bLameEncoderSettingsWidget::slotShowManualSettings() +{ + // save current settings for proper cancellation + bool constant = m_brW->m_radioConstantBitrate->isChecked(); + int constBitrate = m_brW->m_comboConstantBitrate->currentItem(); + int max = m_brW->m_comboMaximumBitrate->currentItem(); + int min = m_brW->m_comboMinimumBitrate->currentItem(); + int av = m_brW->m_spinAverageBitrate->value(); + int mode = m_brW->m_comboMode->currentItem(); + + if( m_manualSettingsDlg->exec() == QDialog::Rejected ) { + m_brW->m_radioConstantBitrate->setChecked( constant ); + m_brW->m_comboConstantBitrate->setCurrentItem( constBitrate ); + m_brW->m_comboMaximumBitrate->setCurrentItem( max ); + m_brW->m_comboMinimumBitrate->setCurrentItem( min ); + m_brW->m_spinAverageBitrate->setValue( av ); + m_brW->m_comboMode->setCurrentItem( mode ); + } + else + updateManualSettingsLabel(); +} + + +void K3bLameEncoderSettingsWidget::updateManualSettingsLabel() +{ + if( m_brW->m_radioConstantBitrate->isChecked() ) + m_w->m_labelManualSettings->setText( i18n("Constant Bitrate: %1 kbps (%2)") + .arg(s_lame_bitrates[m_brW->m_comboConstantBitrate->currentItem()]) + .arg(i18n(s_lame_mode_strings[m_brW->m_comboMode->currentItem()])) ); + else + m_w->m_labelManualSettings->setText( i18n("Variable Bitrate (%1)") + .arg(i18n(s_lame_mode_strings[m_brW->m_comboMode->currentItem()])) ); +} + + +void K3bLameEncoderSettingsWidget::slotQualityLevelChanged( int val ) +{ + m_w->m_labelQualityLevel->setText( i18n(s_lame_preset_strings[val]) ); +} + + +void K3bLameEncoderSettingsWidget::loadConfig() +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3bLameEncoderPlugin" ); + + QString mode = c->readEntry( "Mode", "stereo" ); + if( mode == "stereo" ) + m_brW->m_comboMode->setCurrentItem( 0 ); + else if( mode == "joint" ) + m_brW->m_comboMode->setCurrentItem( 1 ); + else // mono + m_brW->m_comboMode->setCurrentItem( 2 ); + + bool manual = c->readBoolEntry( "Manual Bitrate Settings", false ); + if( manual ) + m_w->m_radioManual->setChecked(true); + else + m_w->m_radioQualityLevel->setChecked(true); + + if( c->readBoolEntry( "VBR", false ) ) + m_brW->m_radioVariableBitrate->setChecked( true ); + else + m_brW->m_radioConstantBitrate->setChecked( true ); + + m_brW->m_comboConstantBitrate->setCurrentItem( i18n("%1 kbps").arg(c->readNumEntry( "Constant Bitrate", 128 )) ); + m_brW->m_comboMaximumBitrate->setCurrentItem( i18n("%1 kbps").arg(c->readNumEntry( "Maximum Bitrate", 224 )) ); + m_brW->m_comboMinimumBitrate->setCurrentItem( i18n("%1 kbps").arg(c->readNumEntry( "Minimum Bitrate", 32 )) ); + m_brW->m_spinAverageBitrate->setValue( c->readNumEntry( "Average Bitrate", 128) ); + + m_brW->m_checkBitrateMaximum->setChecked( c->readBoolEntry( "Use Maximum Bitrate", false ) ); + m_brW->m_checkBitrateMinimum->setChecked( c->readBoolEntry( "Use Minimum Bitrate", false ) ); + m_brW->m_checkBitrateAverage->setChecked( c->readBoolEntry( "Use Average Bitrate", true ) ); + + m_w->m_sliderQuality->setValue( c->readNumEntry( "Quality Level", 5 ) ); + + m_w->m_checkCopyright->setChecked( c->readBoolEntry( "Copyright", false ) ); + m_w->m_checkOriginal->setChecked( c->readBoolEntry( "Original", true ) ); + m_w->m_checkISO->setChecked( c->readBoolEntry( "ISO compliance", false ) ); + m_w->m_checkError->setChecked( c->readBoolEntry( "Error Protection", false ) ); + + // default to 2 which is the same as the -h lame option + m_w->m_spinEncoderQuality->setValue( c->readNumEntry( "Encoder Quality", 7 ) ); + + updateManualSettingsLabel(); +} + + +void K3bLameEncoderSettingsWidget::saveConfig() +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3bLameEncoderPlugin" ); + + QString mode; + switch( m_brW->m_comboMode->currentItem() ) { + case 0: + mode = "stereo"; + break; + case 1: + mode = "joint"; + break; + case 2: + mode = "mono"; + break; + } + c->writeEntry( "Mode", mode ); + + c->writeEntry( "Manual Bitrate Settings", m_w->m_radioManual->isChecked() ); + + c->writeEntry( "VBR", !m_brW->m_radioConstantBitrate->isChecked() ); + c->writeEntry( "Constant Bitrate", m_brW->m_comboConstantBitrate->currentText().left(3).toInt() ); + c->writeEntry( "Maximum Bitrate", m_brW->m_comboMaximumBitrate->currentText().left(3).toInt() ); + c->writeEntry( "Minimum Bitrate", m_brW->m_comboMinimumBitrate->currentText().left(3).toInt() ); + c->writeEntry( "Average Bitrate", m_brW->m_spinAverageBitrate->value() ); + + c->writeEntry( "Use Maximum Bitrate", m_brW->m_checkBitrateMaximum->isChecked() ); + c->writeEntry( "Use Minimum Bitrate", m_brW->m_checkBitrateMinimum->isChecked() ); + c->writeEntry( "Use Average Bitrate", m_brW->m_checkBitrateAverage->isChecked() ); + + c->writeEntry( "Quality Level", m_w->m_sliderQuality->value() ); + + c->writeEntry( "Copyright", m_w->m_checkCopyright->isChecked() ); + c->writeEntry( "Original", m_w->m_checkOriginal->isChecked() ); + c->writeEntry( "ISO compliance", m_w->m_checkISO->isChecked() ); + c->writeEntry( "Error Protection", m_w->m_checkError->isChecked() ); + + // default to 2 which is the same as the -h lame option + c->writeEntry( "Encoder Quality", m_w->m_spinEncoderQuality->value() ); +} + + + +QStringList K3bLameEncoder::extensions() const +{ + return QStringList( "mp3" ); +} + + +QString K3bLameEncoder::fileTypeComment( const QString& ) const +{ + return "MPEG1 Layer III (mp3)"; +} + + +long long K3bLameEncoder::fileSize( const QString&, const K3b::Msf& msf ) const +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3bLameEncoderPlugin" ); + int bitrate = 0; + if( c->readBoolEntry( "Manual Bitrate Settings", false ) ) { + if( c->readBoolEntry( "VBR", false ) ) { + if( c->readBoolEntry( "Use Maximum Bitrate", false ) ) + bitrate = c->readNumEntry( "Maximum Bitrate", 224 ); + if( c->readBoolEntry( "Use Minimum Bitrate", false ) ) + bitrate = ( bitrate > 0 + ? (bitrate - c->readNumEntry( "Minimum Bitrate", 32 )) / 2 + : c->readNumEntry( "Minimum Bitrate", 32 ) ); + if( c->readBoolEntry( "Use Average Bitrate", true ) ) + bitrate = c->readNumEntry( "Average Bitrate", 128 ); + } + else { + bitrate = c->readNumEntry( "Constant Bitrate", 128 ); + } + } + else { + int q = c->readNumEntry( "Quality Level", 5 ); + if( q < 0 ) q = 0; + if( q > 9 ) q = 9; + bitrate = s_lame_preset_approx_bitrates[q]; + } + + return (msf.totalFrames()/75 * bitrate * 1000)/8; +} + + +K3bPluginConfigWidget* K3bLameEncoder::createConfigWidget( QWidget* parent, + const char* name ) const +{ + return new K3bLameEncoderSettingsWidget( parent, name ); +} + + +#include "k3blameencoder.moc" diff --git a/plugins/encoder/lame/k3blameencoder.h b/plugins/encoder/lame/k3blameencoder.h new file mode 100644 index 0000000..424ea64 --- /dev/null +++ b/plugins/encoder/lame/k3blameencoder.h @@ -0,0 +1,88 @@ +/* + * + * $Id: k3blameencoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_LAME_ENCODER_H_ +#define _K3B_LAME_ENCODER_H_ + + +#include <k3baudioencoder.h> +#include <k3bpluginconfigwidget.h> + +#include "base_k3blameencodersettingswidget.h" +#include "base_k3bmanualbitratesettingswidget.h" + + +class KDialogBase; + + +class K3bLameEncoder : public K3bAudioEncoder +{ + Q_OBJECT + + public: + K3bLameEncoder( QObject* parent = 0, const char* name = 0 ); + ~K3bLameEncoder(); + + bool openFile( const QString& extension, const QString& filename, const K3b::Msf& length ); + bool isOpen() const; + void closeFile(); + const QString& filename() const; + + QStringList extensions() const; + + QString fileTypeComment( const QString& ) const; + + long long fileSize( const QString&, const K3b::Msf& msf ) const; + + int pluginSystemVersion() const { return 3; } + + K3bPluginConfigWidget* createConfigWidget( QWidget* parent = 0, + const char* name = 0 ) const; + + private: + void finishEncoderInternal(); + bool initEncoderInternal( const QString& extension, const K3b::Msf& length ); + long encodeInternal( const char* data, Q_ULONG len ); + void setMetaDataInternal( MetaDataField, const QString& ); + + class Private; + Private* d; +}; + + +class K3bLameEncoderSettingsWidget : public K3bPluginConfigWidget +{ + Q_OBJECT + + public: + K3bLameEncoderSettingsWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bLameEncoderSettingsWidget(); + + public slots: + void loadConfig(); + void saveConfig(); + + private slots: + void slotQualityLevelChanged( int val ); + void slotShowManualSettings(); + void updateManualSettingsLabel(); + + private: + base_K3bLameEncoderSettingsWidget* m_w; + base_K3bManualBitrateSettingsWidget* m_brW; + KDialogBase* m_manualSettingsDlg; +}; + +#endif diff --git a/plugins/encoder/lame/k3blameencoder.plugin b/plugins/encoder/lame/k3blameencoder.plugin new file mode 100644 index 0000000..432bc58 --- /dev/null +++ b/plugins/encoder/lame/k3blameencoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3blameencoder +Group=AudioEncoder +Name=K3b Lame Mp3 Encoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=3.0 +Comment=Encoding module to encode MPEG1 Layer III (mp3) files +License=GPL diff --git a/plugins/encoder/ogg/Makefile.am b/plugins/encoder/ogg/Makefile.am new file mode 100644 index 0000000..712adbb --- /dev/null +++ b/plugins/encoder/ogg/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3boggvorbisencoder.la + +libk3boggvorbisencoder_la_SOURCES = base_k3boggvorbisencodersettingswidget.ui k3boggvorbisencoder.cpp + +libk3boggvorbisencoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDECORE) -logg -lvorbis -lvorbisenc +libk3boggvorbisencoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3boggvorbisencoder.plugin + +METASOURCES = AUTO diff --git a/plugins/encoder/ogg/base_k3boggvorbisencodersettingswidget.ui b/plugins/encoder/ogg/base_k3boggvorbisencodersettingswidget.ui new file mode 100644 index 0000000..3e31265 --- /dev/null +++ b/plugins/encoder/ogg/base_k3boggvorbisencodersettingswidget.ui @@ -0,0 +1,392 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>base_K3bOggVorbisEncoderSettingsWidget</class> +<author>Sebastian Trueg</author> +<widget class="QWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>335</width> + <height>271</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>File Quality</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_radioQualityLevel</cstring> + </property> + <property name="text"> + <string>&Quality level:</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Controls the quality of the encoded files</string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>Vorbis' audio quality is not best measured in kilobits per second, but on a scale from -1 to 10 called "quality". <p>For now, quality -1 is roughly equivalent to 45kbps average, 5 is roughly 160kbps, and 10 gives about 400kbps. Most people seeking very-near-CD-quality audio encode at a quality of 5 or, for lossless stereo coupling, 6. The default setting is quality 3, which at approximately 110kbps gives a smaller filesize and significantly better fidelity than .mp3 compression at 128kbps. <p><em>This explanation was copied from the www.vorbis.com FAQ.</em></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_labelQualityLevel</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>textLabel1</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + </hbox> + </widget> + <widget class="QFrame"> + <property name="name"> + <cstring>frame6</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <spacer> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_slideQualityLevel</cstring> + </property> + <property name="minValue"> + <number>-1</number> + </property> + <property name="maxValue"> + <number>10</number> + </property> + <property name="pageStep"> + <number>3</number> + </property> + <property name="value"> + <number>3</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string>high quality</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string>small file</string> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_radioManual</cstring> + </property> + <property name="text"> + <string>M&anual settings:</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>m_checkBitrateUpper</cstring> + </property> + <property name="text"> + <string>&Upper bitrate:</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>m_checkBitrateLower</cstring> + </property> + <property name="text"> + <string>Lower &bitrate:</string> + </property> + </widget> + <widget class="KIntNumInput" row="0" column="1"> + <property name="name"> + <cstring>m_inputBitrateUpper</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KIntNumInput" row="1" column="1"> + <property name="name"> + <cstring>m_inputBitrateNominal</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KIntNumInput" row="2" column="1"> + <property name="name"> + <cstring>m_inputBitrateLower</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>kbps</string> + </property> + </widget> + <widget class="QLabel" row="2" column="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>kbps</string> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>kbps</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>m_checkBitrateNominal</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Nominal bitrate:</string> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>m_radioManual</sender> + <signal>toggled(bool)</signal> + <receiver>frame3</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkBitrateUpper</sender> + <signal>toggled(bool)</signal> + <receiver>m_inputBitrateUpper</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkBitrateNominal</sender> + <signal>toggled(bool)</signal> + <receiver>m_inputBitrateNominal</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkBitrateLower</sender> + <signal>toggled(bool)</signal> + <receiver>m_inputBitrateLower</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_radioQualityLevel</sender> + <signal>toggled(bool)</signal> + <receiver>m_labelQualityLevel</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_radioQualityLevel</sender> + <signal>toggled(bool)</signal> + <receiver>frame6</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/plugins/encoder/ogg/k3boggvorbisencoder.cpp b/plugins/encoder/ogg/k3boggvorbisencoder.cpp new file mode 100644 index 0000000..54e6ecb --- /dev/null +++ b/plugins/encoder/ogg/k3boggvorbisencoder.cpp @@ -0,0 +1,555 @@ +/* + * + * $Id: k3boggvorbisencoder.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3boggvorbisencoder.h" +#include "base_k3boggvorbisencodersettingswidget.h" + +#include <k3bcore.h> +#include <k3bpluginfactory.h> + +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> +#include <knuminput.h> + +#include <qlayout.h> +#include <qradiobutton.h> +#include <qslider.h> +#include <qlcdnumber.h> +#include <qcheckbox.h> +#include <qcstring.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <qlabel.h> + +#include <vorbis/vorbisenc.h> + +// for the random generator +#include <stdlib.h> +#include <time.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3boggvorbisencoder, K3bPluginFactory<K3bOggVorbisEncoder>( "libk3boggvorbisencoder" ) ) + +// quality levels -1 to 10 map to 0 to 11 +static const int s_rough_average_quality_level_bitrates[] = { + 45, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 400 +}; + +// quality levels -1 to 10 map to 0 to 11 +// static const char* s_ogg_quality_level_strings[] = { +// I18N_NOOP("Low quality"), +// I18N_NOOP(""), +// I18N_NOOP(""), +// I18N_NOOP(""), +// I18N_NOOP(""), +// I18N_NOOP("targetted %1 kbps"), +// I18N_NOOP("targetted %1 kbps"), +// I18N_NOOP("targetted %1 kbps"), +// I18N_NOOP(""), +// I18N_NOOP(""), +// I18N_NOOP(""), +// I18N_NOOP(""), +// }; + + +// THIS IS BASED ON THE OGG VORBIS LIB EXAMPLE +// BECAUSE OF THE LACK OF DOCUMENTATION + + +class K3bOggVorbisEncoder::Private +{ +public: + Private() + : manualBitrate(false), + qualityLevel(4), + bitrateUpper(-1), + bitrateNominal(-1), + bitrateLower(-1), + // sampleRate(44100), + oggStream(0), + oggPage(0), + oggPacket(0), + vorbisInfo(0), + vorbisComment(0), + vorbisDspState(0), + vorbisBlock(0), + headersWritten(false) { + } + + // encoding settings + bool manualBitrate; + // 0 to 10 -> 0.0 - 1.0 + int qualityLevel; + int bitrateUpper; + int bitrateNominal; + int bitrateLower; + // int sampleRate; + + // encoding structures + ogg_stream_state *oggStream; // take physical pages, weld into a logical stream of packets + ogg_page *oggPage; // one Ogg bitstream page. Vorbis packets are inside + ogg_packet *oggPacket; // one raw packet of data for decode + vorbis_info *vorbisInfo; // struct that stores all the static vorbis bitstream settings + vorbis_comment *vorbisComment; // struct that stores all the user comments + vorbis_dsp_state *vorbisDspState; // central working state for the packet->PCM decoder + vorbis_block *vorbisBlock; // local working space for packet->PCM decode + + bool headersWritten; +}; + + +K3bOggVorbisEncoder::K3bOggVorbisEncoder( QObject* parent, const char* name ) + : K3bAudioEncoder( parent, name ) +{ + d = new Private(); +} + + +K3bOggVorbisEncoder::~K3bOggVorbisEncoder() +{ + cleanup(); + delete d; +} + + +bool K3bOggVorbisEncoder::initEncoderInternal( const QString&, const K3b::Msf& ) +{ + cleanup(); + + // load user settings + loadConfig(); + + d->oggPage = new ogg_page; + d->oggPacket = new ogg_packet; + d->vorbisInfo = new vorbis_info; + + vorbis_info_init( d->vorbisInfo ); + + int ret = 0; + + if( d->manualBitrate ) { + kdDebug() << "(K3bOggVorbisEncoder) calling: " + << "vorbis_encode_init( d->vorbisInfo, 2, 44100, " + << (d->bitrateUpper != -1 ? d->bitrateUpper*1000 : -1) << ", " + << (d->bitrateNominal != -1 ? d->bitrateNominal*1000 : -1) << ", " + << (d->bitrateLower != -1 ? d->bitrateLower*1000 : -1) << " );" << endl; + + ret = vorbis_encode_init( d->vorbisInfo, + 2, // 2 channels: stereo + 44100, + d->bitrateUpper != -1 ? d->bitrateUpper*1000 : -1, + d->bitrateNominal != -1 ? d->bitrateNominal*1000 : -1, + d->bitrateLower != -1 ? d->bitrateLower*1000 : -1 ); + } + else { + if( d->qualityLevel < -1 ) + d->qualityLevel = -1; + else if( d->qualityLevel > 10 ) + d->qualityLevel = 10; + + kdDebug() << "(K3bOggVorbisEncoder) calling: " + << "vorbis_encode_init_vbr( d->vorbisInfo, 2, 44100, " + << (float)d->qualityLevel/10.0 << ");" << endl; + + ret = vorbis_encode_init_vbr( d->vorbisInfo, + 2, // 2 channels: stereo + 44100, + (float)d->qualityLevel/10.0 ); + } + + if( ret ) { + kdDebug() << "(K3bOggVorbisEncoder) vorbis_encode_init failed: " << ret << endl; + cleanup(); + return false; + } + + // init the comment stuff + d->vorbisComment = new vorbis_comment; + vorbis_comment_init( d->vorbisComment ); + + // add the encoder tag (so everybody knows we did it! ;) + vorbis_comment_add_tag( d->vorbisComment, QCString("ENCODER").data(), QCString("K3bOggVorbisEncoderPlugin").data() ); + + // set up the analysis state and auxiliary encoding storage + d->vorbisDspState = new vorbis_dsp_state; + d->vorbisBlock = new vorbis_block; + vorbis_analysis_init( d->vorbisDspState, d->vorbisInfo ); + vorbis_block_init( d->vorbisDspState, d->vorbisBlock ); + + // set up our packet->stream encoder + // pick a random serial number; that way we can more likely build + // chained streams just by concatenation + d->oggStream = new ogg_stream_state; + srand( time(0) ); + ogg_stream_init( d->oggStream, rand() ); + + return true; +} + + +bool K3bOggVorbisEncoder::writeOggHeaders() +{ + if( !d->oggStream ) { + kdDebug() << "(K3bOggVorbisEncoder) call to writeOggHeaders without init." << endl; + return false; + } + if( d->headersWritten ) { + kdDebug() << "(K3bOggVorbisEncoder) headers already written." << endl; + return true; + } + + // + // Vorbis streams begin with three headers; the initial header (with + // most of the codec setup parameters) which is mandated by the Ogg + // bitstream spec. The second header holds any comment fields. The + // third header holds the bitstream codebook. We merely need to + // make the headers, then pass them to libvorbis one at a time; + // libvorbis handles the additional Ogg bitstream constraints + // + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout( d->vorbisDspState, + d->vorbisComment, + &header, + &header_comm, + &header_code); + + // automatically placed in its own page + ogg_stream_packetin( d->oggStream, &header ); + ogg_stream_packetin( d->oggStream, &header_comm ); + ogg_stream_packetin( d->oggStream, &header_code ); + + // + // This ensures the actual + // audio data will start on a new page, as per spec + // + QByteArray data; + while( ogg_stream_flush( d->oggStream, d->oggPage ) ) { + writeData( (char*)d->oggPage->header, d->oggPage->header_len ); + writeData( (char*)d->oggPage->body, d->oggPage->body_len ); + } + + d->headersWritten = true; + + return true; +} + + +long K3bOggVorbisEncoder::encodeInternal( const char* data, Q_ULONG len ) +{ + if( !d->headersWritten ) + if( !writeOggHeaders() ) + return -1; + + // expose the buffer to submit data + float** buffer = vorbis_analysis_buffer( d->vorbisDspState, len/4 ); + + // uninterleave samples + unsigned long i = 0; + for( i = 0; i < len/4; i++ ) { + buffer[0][i]=( (data[i*4+1]<<8) | (0x00ff&(int)data[i*4]) ) / 32768.f; + buffer[1][i]=( (data[i*4+3]<<8) | (0x00ff&(int)data[i*4+2]) ) / 32768.f; + } + + // tell the library how much we actually submitted + vorbis_analysis_wrote( d->vorbisDspState, i ); + + return flushVorbis(); +} + + +long K3bOggVorbisEncoder::flushVorbis() +{ + // vorbis does some data preanalysis, then divvies up blocks for + // more involved (potentially parallel) processing. Get a single + // block for encoding now + long writtenData = 0; + while( vorbis_analysis_blockout( d->vorbisDspState, d->vorbisBlock ) == 1 ) { + + // analysis + vorbis_analysis( d->vorbisBlock, 0 ); + vorbis_bitrate_addblock( d->vorbisBlock ); + + while( vorbis_bitrate_flushpacket( d->vorbisDspState, d->oggPacket ) ) { + + // weld the packet into the bitstream + ogg_stream_packetin( d->oggStream, d->oggPacket ); + + // write out pages (if any) + while( ogg_stream_pageout( d->oggStream, d->oggPage ) ) { + writeData( (char*)d->oggPage->header, d->oggPage->header_len ); + writeData( (char*)d->oggPage->body, d->oggPage->body_len ); + + writtenData += ( d->oggPage->header_len + d->oggPage->body_len ); + } + } + } + + return writtenData; +} + + +void K3bOggVorbisEncoder::finishEncoderInternal() +{ + if( d->vorbisDspState ) { + vorbis_analysis_wrote( d->vorbisDspState, 0 ); + flushVorbis(); + } + else + kdDebug() << "(K3bOggVorbisEncoder) call to finishEncoderInternal without init." << endl; +} + + +void K3bOggVorbisEncoder::setMetaDataInternal( K3bAudioEncoder::MetaDataField f, const QString& value ) +{ + if( d->vorbisComment ) { + QCString key; + + switch( f ) { + case META_TRACK_TITLE: + key = "TITLE"; + break; + case META_TRACK_ARTIST: + key = "ARTIST"; + break; + case META_ALBUM_TITLE: + key = "ALBUM"; + break; + case META_ALBUM_COMMENT: + key = "DESCRIPTION"; + break; + case META_YEAR: + key = "DATE"; + break; + case META_TRACK_NUMBER: + key = "TRACKNUMBER"; + break; + case META_GENRE: + key = "GENRE"; + break; + default: + return; + } + + vorbis_comment_add_tag( d->vorbisComment, key.data(), value.utf8().data() ); + } + else + kdDebug() << "(K3bOggVorbisEncoder) call to setMetaDataInternal without init." << endl; +} + + +void K3bOggVorbisEncoder::cleanup() +{ + if( d->oggStream ) { + ogg_stream_clear( d->oggStream ); + delete d->oggStream; + d->oggStream = 0; + } + if( d->vorbisBlock ) { + vorbis_block_clear( d->vorbisBlock ); + delete d->vorbisBlock; + d->vorbisBlock = 0; + } + if( d->vorbisDspState ) { + vorbis_dsp_clear( d->vorbisDspState ); + delete d->vorbisDspState; + d->vorbisDspState = 0; + } + if( d->vorbisComment ) { + vorbis_comment_clear( d->vorbisComment ); + delete d->vorbisComment; + d->vorbisComment = 0; + } + if( d->vorbisInfo ) { + vorbis_info_clear( d->vorbisInfo ); + delete d->vorbisInfo; + d->vorbisInfo = 0; + } + + // ogg_page and ogg_packet structs always point to storage in + // libvorbis. They're never freed or manipulated directly + if( d->oggPage ) { + delete d->oggPage; + d->oggPage = 0; + } + if( d->oggPacket ) { + delete d->oggPacket; + d->oggPacket = 0; + } + + d->headersWritten = false; +} + + +void K3bOggVorbisEncoder::loadConfig() +{ + KConfig* c = k3bcore->config(); + + c->setGroup( "K3bOggVorbisEncoderPlugin" ); + + d->manualBitrate = c->readBoolEntry( "manual bitrate", false ); + d->qualityLevel = c->readNumEntry( "quality level", 4 ); + d->bitrateUpper = c->readNumEntry( "bitrate upper", -1 ); + d->bitrateNominal = c->readNumEntry( "bitrate nominal", -1 ); + d->bitrateLower = c->readNumEntry( "bitrate lower", -1 ); + // d->sampleRate = c->readNumEntry( "samplerate", 44100 ); +} + + + + +K3bOggVorbisEncoderSettingsWidget::K3bOggVorbisEncoderSettingsWidget( QWidget* parent, const char* name ) + : K3bPluginConfigWidget( parent, name ) +{ + w = new base_K3bOggVorbisEncoderSettingsWidget( this ); + + QString ttQuality = i18n("Controls the quality of the encoded files."); + QString wsQuality = i18n("<p>Vorbis' audio quality is not best measured in kilobits per second, " + "but on a scale from -1 to 10 called <em>quality</em>." + "<p>For now, quality -1 is roughly equivalent to 45kbps average, " + "5 is roughly 160kbps, and 10 gives about 400kbps. " + "Most people seeking very-near-CD-quality audio encode at a quality of 5 or, " + "for lossless stereo coupling, 6. The quality 3 gives, at " + "approximately 110kbps a smaller filesize and significantly better fidelity " + "than .mp3 compression at 128kbps." + "<p><em>This explanation is based on the one from the www.vorbis.com FAQ.</em>"); + + QToolTip::add( w->m_radioQualityLevel, ttQuality ); + QToolTip::add( w->m_labelQualityLevel, ttQuality ); + QToolTip::add( w->m_slideQualityLevel, ttQuality ); + QWhatsThis::add( w->m_radioQualityLevel, wsQuality ); + QWhatsThis::add( w->m_labelQualityLevel, wsQuality ); + QWhatsThis::add( w->m_slideQualityLevel, wsQuality ); + + + QHBoxLayout* lay = new QHBoxLayout( this ); + lay->setMargin( 0 ); + + lay->addWidget( w ); + + connect( w->m_slideQualityLevel, SIGNAL(valueChanged(int)), + this, SLOT(slotQualityLevelChanged(int)) ); + + slotQualityLevelChanged( 4 ); +} + + +K3bOggVorbisEncoderSettingsWidget::~K3bOggVorbisEncoderSettingsWidget() +{ +} + + +void K3bOggVorbisEncoderSettingsWidget::slotQualityLevelChanged( int val ) +{ + w->m_labelQualityLevel->setText( QString::number(val) + " " + + i18n("(targetted VBR of %1)").arg(s_rough_average_quality_level_bitrates[val+1]) ); +} + + +void K3bOggVorbisEncoderSettingsWidget::loadConfig() +{ + KConfig* c = k3bcore->config(); + + c->setGroup( "K3bOggVorbisEncoderPlugin" ); + + if( c->readBoolEntry( "manual bitrate", false ) ) + w->m_radioManual->setChecked(true); + else + w->m_radioQualityLevel->setChecked(true); + w->m_slideQualityLevel->setValue( c->readNumEntry( "quality level", 4 ) ); + w->m_inputBitrateUpper->setValue( c->readNumEntry( "bitrate upper", -1 ) ); + w->m_checkBitrateUpper->setChecked( c->readNumEntry( "bitrate upper", -1 ) != -1 ); + w->m_inputBitrateNominal->setValue( c->readNumEntry( "bitrate nominal", -1 ) ); + w->m_checkBitrateNominal->setChecked( c->readNumEntry( "bitrate nominal", -1 ) != -1 ); + w->m_inputBitrateLower->setValue( c->readNumEntry( "bitrate lower", -1 ) ); + w->m_checkBitrateLower->setChecked( c->readNumEntry( "bitrate lower", -1 ) != -1 ); + // w->m_inputSamplerate->setValue( c->readNumEntry( "samplerate", 44100 ) ); +} + + +void K3bOggVorbisEncoderSettingsWidget::saveConfig() +{ + KConfig* c = k3bcore->config(); + + c->setGroup( "K3bOggVorbisEncoderPlugin" ); + + c->writeEntry( "manual bitrate", w->m_radioManual->isChecked() ); + c->writeEntry( "quality level", w->m_slideQualityLevel->value() ); + c->writeEntry( "bitrate upper", w->m_checkBitrateUpper->isChecked() ? w->m_inputBitrateUpper->value() : -1 ); + c->writeEntry( "bitrate nominal", w->m_checkBitrateNominal->isChecked() ? w->m_inputBitrateNominal->value() : -1 ); + c->writeEntry( "bitrate lower", w->m_checkBitrateLower->isChecked() ? w->m_inputBitrateLower->value() : -1 ); + // c->writeEntry( "samplerate", w->m_inputSamplerate->value() ); +} + + +QString K3bOggVorbisEncoder::fileTypeComment( const QString& ) const +{ + return i18n("Ogg-Vorbis"); +} + + +long long K3bOggVorbisEncoder::fileSize( const QString&, const K3b::Msf& msf ) const +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3bOggVorbisEncoderPlugin" ); + + // the following code is based on the size estimation from the audiocd kioslave + // TODO: reimplement. + + if( !c->readBoolEntry( "manual bitrate", false ) ) { + // Estimated numbers based on the Vorbis FAQ: + // http://www.xiph.org/archives/vorbis-faq/200203/0030.html + +// static long vorbis_q_bitrate[] = { 45, 60, 74, 86, 106, 120, 152, +// 183, 207, 239, 309, 440 }; + + int qualityLevel = c->readNumEntry( "quality level", 4 ); + + if( qualityLevel < -1 ) + qualityLevel = -1; + else if( qualityLevel > 10 ) + qualityLevel = 10; + return ( (msf.totalFrames()/75) * s_rough_average_quality_level_bitrates[qualityLevel+1] * 1000 ) / 8; + } + else { + return (msf.totalFrames()/75) * c->readNumEntry( "bitrate nominal", 160 ) * 1000 / 8; + } +} + + +K3bPluginConfigWidget* K3bOggVorbisEncoder::createConfigWidget( QWidget* parent, + const char* name ) const +{ + return new K3bOggVorbisEncoderSettingsWidget( parent, name ); +} + + +#include "k3boggvorbisencoder.moc" diff --git a/plugins/encoder/ogg/k3boggvorbisencoder.h b/plugins/encoder/ogg/k3boggvorbisencoder.h new file mode 100644 index 0000000..0148713 --- /dev/null +++ b/plugins/encoder/ogg/k3boggvorbisencoder.h @@ -0,0 +1,81 @@ +/* + * + * $Id: k3boggvorbisencoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_OGG_VORBIS_ENCODER_H_ +#define _K3B_OGG_VORBIS_ENCODER_H_ + + +#include <k3baudioencoder.h> +#include <k3bpluginconfigwidget.h> + + +class base_K3bOggVorbisEncoderSettingsWidget; + + +class K3bOggVorbisEncoder : public K3bAudioEncoder +{ + Q_OBJECT + + public: + K3bOggVorbisEncoder( QObject* parent = 0, const char* name = 0 ); + ~K3bOggVorbisEncoder(); + + QStringList extensions() const { return QStringList("ogg"); } + + QString fileTypeComment( const QString& ) const; + + long long fileSize( const QString&, const K3b::Msf& msf ) const; + + int pluginSystemVersion() const { return 3; } + + K3bPluginConfigWidget* createConfigWidget( QWidget* parent = 0, + const char* name = 0 ) const; + + private: + void loadConfig(); + void finishEncoderInternal(); + bool initEncoderInternal( const QString& extension, const K3b::Msf& length ); + long encodeInternal( const char* data, Q_ULONG len ); + void setMetaDataInternal( MetaDataField, const QString& ); + + bool writeOggHeaders(); + void cleanup(); + long flushVorbis(); + + class Private; + Private* d; +}; + + +class K3bOggVorbisEncoderSettingsWidget : public K3bPluginConfigWidget +{ + Q_OBJECT + + public: + K3bOggVorbisEncoderSettingsWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bOggVorbisEncoderSettingsWidget(); + + public slots: + void loadConfig(); + void saveConfig(); + + private slots: + void slotQualityLevelChanged( int val ); + + private: + base_K3bOggVorbisEncoderSettingsWidget* w; +}; + +#endif diff --git a/plugins/encoder/ogg/k3boggvorbisencoder.plugin b/plugins/encoder/ogg/k3boggvorbisencoder.plugin new file mode 100644 index 0000000..2f73e8e --- /dev/null +++ b/plugins/encoder/ogg/k3boggvorbisencoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3boggvorbisencoder +Group=AudioEncoder +Name=K3b Ogg Vorbis Encoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=2.1 +Comment=Encoding module to encode Ogg Vorbis files +License=GPL diff --git a/plugins/encoder/skeleton.cpp b/plugins/encoder/skeleton.cpp new file mode 100644 index 0000000..7a46caf --- /dev/null +++ b/plugins/encoder/skeleton.cpp @@ -0,0 +1,123 @@ +/* + * + * $Id: skeleton.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3b<name>encoder.h" + +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3b<name>encoder, K3bPluginFactory<K3b<name>Encoder>( "libk3b<name>encoder" ) ) + + +K3b<name>Encoder::K3b<name>Encoder( QObject* parent, const char* name ) + : K3bAudioEncoder( parent, name ) +{ +} + + +K3b<name>Encoder::~K3b<name>Encoder() +{ +} + + +bool K3b<name>Encoder::initEncoderInternal( const QString& ) +{ + // PUT YOUR CODE HERE + return false; +} + + +long K3b<name>Encoder::encodeInternal( const char* data, Q_ULONG len ) +{ + // PUT YOUR CODE HERE + return false; +} + + +void K3b<name>Encoder::finishEncoderInternal() +{ + // PUT YOUR CODE HERE +} + + +void K3b<name>Encoder::setMetaDataInternal( K3bAudioEncoder::MetaDataField f, const QString& value ) +{ + // PUT YOUR CODE HERE +} + + +QStringList K3b<name>Encoder::extensions() const +{ + // PUT YOUR CODE HERE + return QStringList( "" ); +} + + +QString K3b<name>Encoder::fileTypeComment( const QString& ) const +{ + // PUT YOUR CODE HERE + return ""; +} + + +long long K3b<name>Encoder::fileSize( const QString&, const K3b::Msf& msf ) const +{ + // PUT YOUR CODE HERE + return -1; +} + + +K3bPluginConfigWidget* K3b<name>Encoder::createConfigWidget( QWidget* parent, + const char* name ) const +{ + return new K3b<name>EncoderSettingsWidget( parent, name ); +} + + + +K3b<name>EncoderSettingsWidget::K3b<name>EncoderSettingsWidget( QWidget* parent, const char* name ) + : K3bPluginConfigWidget( parent, name ) +{ +} + + +K3b<name>EncoderSettingsWidget::~K3b<name>EncoderSettingsWidget() +{ +} + + +void K3b<name>EncoderSettingsWidget::loadConfig() +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3b<name>EncoderPlugin" ); + + // PUT YOUR CODE HERE +} + + +void K3b<name>EncoderSettingsWidget::saveConfig() +{ + KConfig* c = k3bcore->config(); + c->setGroup( "K3b<name>EncoderPlugin" ); + + // PUT YOUR CODE HERE +} + + +#include "k3b<name>encoder.moc" diff --git a/plugins/encoder/skeleton.h b/plugins/encoder/skeleton.h new file mode 100644 index 0000000..78c733e --- /dev/null +++ b/plugins/encoder/skeleton.h @@ -0,0 +1,64 @@ +/* + * + * $Id: skeleton.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_<name>_ENCODER_H_ +#define _K3B_<name>_ENCODER_H_ + + +#include <k3baudioencoder.h> +#include <k3bpluginconfigwidget.h> + + +class K3b<name>Encoder : public K3bAudioEncoder +{ + Q_OBJECT + + public: + K3b<name>Encoder( QObject* parent = 0, const char* name = 0 ); + ~K3b<name>Encoder(); + + QStringList extensions() const; + + QString fileTypeComment( const QString& ) const; + + long long fileSize( const QString&, const K3b::Msf& msf ) const; + + int pluginSystemVersion() const { return 3; } + + K3bPluginConfigWidget* createConfigWidget( QWidget* parent = 0, + const char* name = 0 ) const; + + private: + void finishEncoderInternal(); + bool initEncoderInternal( const QString& extension ); + long encodeInternal( const char* data, Q_ULONG len ); + void setMetaDataInternal( MetaDataField, const QString& ); +}; + + +class K3b<name>EncoderSettingsWidget : public K3bPluginConfigWidget +{ + Q_OBJECT + + public: + K3b<name>EncoderSettingsWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3b<name>EncoderSettingsWidget(); + + public slots: + void loadConfig(); + void saveConfig(); +}; + +#endif diff --git a/plugins/encoder/skeleton.plugin b/plugins/encoder/skeleton.plugin new file mode 100644 index 0000000..cf178b7 --- /dev/null +++ b/plugins/encoder/skeleton.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3b<name>encoder +Group=AudioEncoder +Name=K3b ??? Encoder +Author=??? +Email=??? +Version=?? +Comment=Encoding module to encode <name> files +License=??? diff --git a/plugins/encoder/sox/Makefile.am b/plugins/encoder/sox/Makefile.am new file mode 100644 index 0000000..535b1c0 --- /dev/null +++ b/plugins/encoder/sox/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3b/plugin -I$(srcdir)/../../../libk3b/core -I$(srcdir)/../../../libk3bdevice $(all_includes) + +kde_module_LTLIBRARIES = libk3bsoxencoder.la + +libk3bsoxencoder_la_SOURCES = base_k3bsoxencoderconfigwidget.ui k3bsoxencoder.cpp + +libk3bsoxencoder_la_LIBADD = ../../../libk3b/libk3b.la $(LIB_KDECORE) +libk3bsoxencoder_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3bsoxencoder.plugin + +METASOURCES = AUTO diff --git a/plugins/encoder/sox/base_k3bsoxencoderconfigwidget.ui b/plugins/encoder/sox/base_k3bsoxencoderconfigwidget.ui new file mode 100644 index 0000000..7d8ee43 --- /dev/null +++ b/plugins/encoder/sox/base_k3bsoxencoderconfigwidget.ui @@ -0,0 +1,258 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>base_K3bSoxEncoderConfigWidget</class> +<author>Sebastian Trueg</author> +<widget class="QWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>419</width> + <height>201</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkManual</cstring> + </property> + <property name="text"> + <string>Manual settings (used for all file types)</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>10</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Sample rate:</string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Signed Linear</string> + </property> + </item> + <item> + <property name="text"> + <string>Unsigned Linear</string> + </property> + </item> + <item> + <property name="text"> + <string>u-law (logarithmic)</string> + </property> + </item> + <item> + <property name="text"> + <string>A-law (logarithmic)</string> + </property> + </item> + <item> + <property name="text"> + <string>ADPCM</string> + </property> + </item> + <item> + <property name="text"> + <string>IMA_ADPCM</string> + </property> + </item> + <item> + <property name="text"> + <string>GSM</string> + </property> + </item> + <item> + <property name="text"> + <string>Floating-Point</string> + </property> + </item> + <property name="name"> + <cstring>m_comboEncoding</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="whatsThis" stdset="0"> + <string><p>The sample data encoding is signed linear (2's complement), unsigned linear, u-law (logarithmic), A-law (logarithmic), ADPCM, IMA_ADPCM, GSM, or Floating-point.</p> +<p><b>U-law</b> (actually shorthand for mu-law) and <b>A-law</b> are the U.S. and international standards for logarithmic telephone sound compression. When uncompressed u-law has roughly the precision of 14-bit PCM audio and A-law has roughly the precision of 13-bit PCM audio. A-law and u-law data is sometimes encoded using a reversed bit-ordering (i.e. MSB becomes LSB).<br> <b>ADPCM </b> is a form of sound compression that has a good compromise between good sound quality and fast encoding/decoding time. It is used for telephone sound compression and places where full fidelity is not as important. When uncompressed it has roughly the precision of 16-bit PCM audio. Popular versions of ADPCM include G.726, MS ADPCM, and IMA ADPCM. It has different meanings in different file handlers. In .wav files it represents MS ADPCM files, in all others it means G.726 ADPCM. <br> <b>IMA ADPCM</b> is a specific form of ADPCM compression, slightly simpler and slightly lower fidelity than Microsoft's flavor of ADPCM. IMA ADPCM is also called DVI ADPCM.<br> <b>GSM</b> is a standard used for telephone sound compression in European countries and is gaining popularity because of its good quality. It is usually CPU intensive to work with GSM audio data.</p> <p><em>Description based on the SoX manpage</em></p></string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_editSamplerate</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>14400</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Data size:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Data encoding:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Channels:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>1 (mono)</string> + </property> + </item> + <item> + <property name="text"> + <string>2 (stereo)</string> + </property> + </item> + <item> + <property name="text"> + <string>4 (quad sound)</string> + </property> + </item> + <property name="name"> + <cstring>m_comboChannels</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>Bytes</string> + </property> + </item> + <item> + <property name="text"> + <string>16-bit Words</string> + </property> + </item> + <item> + <property name="text"> + <string>32-bit Words</string> + </property> + </item> + <property name="name"> + <cstring>m_comboSize</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>250</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<connections> + <connection> + <sender>m_checkManual</sender> + <signal>toggled(bool)</signal> + <receiver>m_comboChannels</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkManual</sender> + <signal>toggled(bool)</signal> + <receiver>m_editSamplerate</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkManual</sender> + <signal>toggled(bool)</signal> + <receiver>m_comboEncoding</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_checkManual</sender> + <signal>toggled(bool)</signal> + <receiver>m_comboSize</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/plugins/encoder/sox/k3bsoxencoder.cpp b/plugins/encoder/sox/k3bsoxencoder.cpp new file mode 100644 index 0000000..ecae2e4 --- /dev/null +++ b/plugins/encoder/sox/k3bsoxencoder.cpp @@ -0,0 +1,482 @@ +/* + * + * $Id: k3bsoxencoder.cpp 731310 2007-10-31 10:30:05Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3bsoxencoder.h" +#include "base_k3bsoxencoderconfigwidget.h" + +#include <k3bprocess.h> +#include <k3bcore.h> +#include <k3bexternalbinmanager.h> +#include <k3bpluginfactory.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> + +#include <qfileinfo.h> +#include <qfile.h> +#include <qvalidator.h> +#include <qlineedit.h> +#include <qcombobox.h> +#include <qcheckbox.h> +#include <qlayout.h> + +#include <sys/types.h> +#include <sys/wait.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3bsoxencoder, K3bPluginFactory<K3bSoxEncoder>( "libk3bsoxencoder" ) ) + + +// the sox external program +class K3bSoxProgram : public K3bExternalProgram +{ + public: + K3bSoxProgram() + : K3bExternalProgram( "sox" ) { + } + + bool scan( const QString& p ) { + if( p.isEmpty() ) + return false; + + QString path = p; + QFileInfo fi( path ); + if( fi.isDir() ) { + if( path[path.length()-1] != '/' ) + path.append("/"); + path.append("sox"); + } + + if( !QFile::exists( path ) ) + return false; + + K3bExternalBin* bin = 0; + + // probe version + KProcess vp; + K3bProcessOutputCollector out( &vp ); + + vp << path << "-h"; + if( vp.start( KProcess::Block, KProcess::AllOutput ) ) { + int pos = out.output().find( "sox: SoX Version" ); + if ( pos < 0 ) + pos = out.output().find( "sox: SoX v" ); // newer sox versions + int endPos = out.output().find( "\n", pos ); + if( pos > 0 && endPos > 0 ) { + pos += 17; + bin = new K3bExternalBin( this ); + bin->path = path; + bin->version = out.output().mid( pos, endPos-pos ); + + addBin( bin ); + + return true; + } + else { + pos = out.output().find( "sox: Version" ); + endPos = out.output().find( "\n", pos ); + if( pos > 0 && endPos > 0 ) { + pos += 13; + bin = new K3bExternalBin( this ); + bin->path = path; + bin->version = out.output().mid( pos, endPos-pos ); + + addBin( bin ); + + return true; + } + else + return false; + } + } + else + return false; + } +}; + + +class K3bSoxEncoder::Private +{ +public: + Private() + : process(0) { + } + + K3bProcess* process; + QString fileName; +}; + + +K3bSoxEncoder::K3bSoxEncoder( QObject* parent, const char* name ) + : K3bAudioEncoder( parent, name ) +{ + if( k3bcore->externalBinManager()->program( "sox" ) == 0 ) + k3bcore->externalBinManager()->addProgram( new K3bSoxProgram() ); + + d = new Private(); +} + + +K3bSoxEncoder::~K3bSoxEncoder() +{ + delete d->process; + delete d; +} + + +void K3bSoxEncoder::finishEncoderInternal() +{ + if( d->process ) { + if( d->process->isRunning() ) { + ::close( d->process->stdinFd() ); + + // this is kind of evil... + // but we need to be sure the process exited when this method returnes + ::waitpid( d->process->pid(), 0, 0 ); + } + } +} + + +void K3bSoxEncoder::slotSoxFinished( KProcess* p ) +{ + if( !p->normalExit() || p->exitStatus() != 0 ) + kdDebug() << "(K3bSoxEncoder) sox exited with error." << endl; +} + + +bool K3bSoxEncoder::openFile( const QString& ext, const QString& filename, const K3b::Msf& ) +{ + d->fileName = filename; + return initEncoderInternal( ext ); +} + + +void K3bSoxEncoder::closeFile() +{ + finishEncoderInternal(); +} + + +bool K3bSoxEncoder::initEncoderInternal( const QString& extension ) +{ + const K3bExternalBin* soxBin = k3bcore->externalBinManager()->binObject( "sox" ); + if( soxBin ) { + delete d->process; + d->process = new K3bProcess(); + d->process->setSplitStdout(true); + d->process->setRawStdin(true); + + connect( d->process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotSoxFinished(KProcess*)) ); + connect( d->process, SIGNAL(stderrLine(const QString&)), + this, SLOT(slotSoxOutputLine(const QString&)) ); + connect( d->process, SIGNAL(stdoutLine(const QString&)), + this, SLOT(slotSoxOutputLine(const QString&)) ); + + // input settings + *d->process << soxBin->path + << "-t" << "raw" // raw samples + << "-r" << "44100" // samplerate + << "-s" // signed linear + << "-w" // 16-bit words + << "-c" << "2" // stereo + << "-"; // read from stdin + + // output settings + *d->process << "-t" << extension; + + KConfig* c = k3bcore->config(); + c->setGroup( "K3bSoxEncoderPlugin" ); + if( c->readBoolEntry( "manual settings", false ) ) { + *d->process << "-r" << QString::number( c->readNumEntry( "samplerate", 44100 ) ) + << "-c" << QString::number( c->readNumEntry( "channels", 2 ) ); + + int size = c->readNumEntry( "data size", 16 ); + *d->process << ( size == 8 ? QString("-b") : ( size == 32 ? QString("-l") : QString("-w") ) ); + + QString encoding = c->readEntry( "data encoding", "signed" ); + if( encoding == "unsigned" ) + *d->process << "-u"; + else if( encoding == "u-law" ) + *d->process << "-U"; + else if( encoding == "A-law" ) + *d->process << "-A"; + else if( encoding == "ADPCM" ) + *d->process << "-a"; + else if( encoding == "IMA_ADPCM" ) + *d->process << "-i"; + else if( encoding == "GSM" ) + *d->process << "-g"; + else if( encoding == "Floating-point" ) + *d->process << "-f"; + else + *d->process << "-s"; + } + + *d->process << d->fileName; + + kdDebug() << "***** sox parameters:" << endl; + const QValueList<QCString>& args = d->process->args(); + QString s; + for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << flush << endl; + + + return d->process->start( KProcess::NotifyOnExit, KProcess::All ); + } + else { + kdDebug() << "(K3bSoxEncoder) could not find sox bin." << endl; + return false; + } +} + + +long K3bSoxEncoder::encodeInternal( const char* data, Q_ULONG len ) +{ + if( d->process ) { + if( d->process->isRunning() ) + return ::write( d->process->stdinFd(), (const void*)data, len ); + else + return -1; + } + else + return -1; +} + + +void K3bSoxEncoder::slotSoxOutputLine( const QString& line ) +{ + kdDebug() << "(sox) " << line << endl; +} + + +QStringList K3bSoxEncoder::extensions() const +{ + static QStringList s_extensions; + if( s_extensions.isEmpty() ) { + s_extensions << "au" + << "8svx" + << "aiff" + << "avr" + << "cdr" + << "cvs" + << "dat" + << "gsm" + << "hcom" + << "maud" + << "sf" + << "sph" + << "smp" + << "txw" + << "vms" + << "voc" + << "wav" + << "wve" + << "raw"; + } + + if( k3bcore->externalBinManager()->foundBin( "sox" ) ) + return s_extensions; + else + return QStringList(); // no sox -> no encoding +} + + +QString K3bSoxEncoder::fileTypeComment( const QString& ext ) const +{ + if( ext == "au" ) + return i18n("Sun AU"); + else if( ext == "8svx" ) + return i18n("Amiga 8SVX"); + else if( ext == "aiff" ) + return i18n("AIFF"); + else if( ext == "avr" ) + return i18n("Audio Visual Research"); + else if( ext == "cdr" ) + return i18n("CD-R"); + else if( ext == "cvs" ) + return i18n("CVS"); + else if( ext == "dat" ) + return i18n("Text Data"); + else if( ext == "gsm" ) + return i18n("GSM Speech"); + else if( ext == "hcom" ) + return i18n("Macintosh HCOM"); + else if( ext == "maud" ) + return i18n("Maud (Amiga)"); + else if( ext == "sf" ) + return i18n("IRCAM"); + else if( ext == "sph" ) + return i18n("SPHERE"); + else if( ext == "smp" ) + return i18n("Turtle Beach SampleVision"); + else if( ext == "txw" ) + return i18n("Yamaha TX-16W"); + else if( ext == "vms" ) + return i18n("VMS"); + else if( ext == "voc" ) + return i18n("Sound Blaster VOC"); + else if( ext == "wav" ) + return i18n("Wave (Sox)"); + else if( ext == "wve" ) + return i18n("Psion 8-bit A-law"); + else if( ext == "raw" ) + return i18n("Raw"); + else + return i18n("Error"); +} + + +long long K3bSoxEncoder::fileSize( const QString&, const K3b::Msf& msf ) const +{ + // for now we make a rough assumption based on the settings + KConfig* c = k3bcore->config(); + c->setGroup( "K3bSoxEncoderPlugin" ); + if( c->readBoolEntry( "manual settings", false ) ) { + int sr = c->readNumEntry( "samplerate", 44100 ); + int ch = c->readNumEntry( "channels", 2 ); + int wsize = c->readNumEntry( "data size", 16 ); + + return msf.totalFrames()*sr*ch*wsize/75; + } + else { + // fallback to raw + return msf.audioBytes(); + } +} + + +K3bPluginConfigWidget* K3bSoxEncoder::createConfigWidget( QWidget* parent, + const char* name ) const +{ + return new K3bSoxEncoderSettingsWidget( parent, name ); +} + + + +K3bSoxEncoderSettingsWidget::K3bSoxEncoderSettingsWidget( QWidget* parent, const char* name ) + : K3bPluginConfigWidget( parent, name ) +{ + w = new base_K3bSoxEncoderConfigWidget( this ); + w->m_editSamplerate->setValidator( new QIntValidator( w->m_editSamplerate ) ); + + QHBoxLayout* lay = new QHBoxLayout( this ); + lay->setMargin( 0 ); + + lay->addWidget( w ); +} + + +K3bSoxEncoderSettingsWidget::~K3bSoxEncoderSettingsWidget() +{ +} + + +void K3bSoxEncoderSettingsWidget::loadConfig() +{ + KConfig* c = k3bcore->config(); + + c->setGroup( "K3bSoxEncoderPlugin" ); + + w->m_checkManual->setChecked( c->readBoolEntry( "manual settings", false ) ); + + int channels = c->readNumEntry( "channels", 2 ); + w->m_comboChannels->setCurrentItem( channels == 4 ? 2 : channels-1 ); + + w->m_editSamplerate->setText( QString::number( c->readNumEntry( "samplerate", 44100 ) ) ); + + QString encoding = c->readEntry( "data encoding", "signed" ); + if( encoding == "unsigned" ) + w->m_comboEncoding->setCurrentItem(1); + else if( encoding == "u-law" ) + w->m_comboEncoding->setCurrentItem(2); + else if( encoding == "A-law" ) + w->m_comboEncoding->setCurrentItem(3); + else if( encoding == "ADPCM" ) + w->m_comboEncoding->setCurrentItem(4); + else if( encoding == "IMA_ADPCM" ) + w->m_comboEncoding->setCurrentItem(5); + else if( encoding == "GSM" ) + w->m_comboEncoding->setCurrentItem(6); + else if( encoding == "Floating-point" ) + w->m_comboEncoding->setCurrentItem(7); + else + w->m_comboEncoding->setCurrentItem(0); + + int size = c->readNumEntry( "data size", 16 ); + w->m_comboSize->setCurrentItem( size == 8 ? 0 : ( size == 32 ? 2 : 1 ) ); +} + + +void K3bSoxEncoderSettingsWidget::saveConfig() +{ + KConfig* c = k3bcore->config(); + + c->setGroup( "K3bSoxEncoderPlugin" ); + + c->writeEntry( "manual settings", w->m_checkManual->isChecked() ); + + c->writeEntry( "channels", w->m_comboChannels->currentItem() == 0 + ? 1 + : ( w->m_comboChannels->currentItem() == 2 + ? 4 + : 2 ) ); + + c->writeEntry( "data size", w->m_comboSize->currentItem() == 0 + ? 8 + : ( w->m_comboSize->currentItem() == 2 + ? 32 + : 16 ) ); + + c->writeEntry( "samplerate", w->m_editSamplerate->text().toInt() ); + + QString enc; + switch( w->m_comboEncoding->currentItem() ) { + case 1: + enc = "unsigned"; + break; + case 2: + enc = "u-law"; + break; + case 3: + enc = "A-law"; + break; + case 4: + enc = "ADPCM"; + break; + case 5: + enc = "IMA_ADPCM"; + break; + case 6: + enc = "GSM"; + break; + case 7: + enc = "Floating-point"; + break; + default: + enc = "signed"; + break; + } + c->writeEntry( "data encoding", enc ); +} + + +#include "k3bsoxencoder.moc" diff --git a/plugins/encoder/sox/k3bsoxencoder.h b/plugins/encoder/sox/k3bsoxencoder.h new file mode 100644 index 0000000..555f44e --- /dev/null +++ b/plugins/encoder/sox/k3bsoxencoder.h @@ -0,0 +1,82 @@ +/* + * + * $Id: k3bsoxencoder.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_SOX_ENCODER_H_ +#define _K3B_SOX_ENCODER_H_ + + +#include <k3baudioencoder.h> +#include <k3bpluginconfigwidget.h> + + +class base_K3bSoxEncoderConfigWidget; +class KProcess; + +class K3bSoxEncoder : public K3bAudioEncoder +{ + Q_OBJECT + + public: + K3bSoxEncoder( QObject* parent = 0, const char* name = 0 ); + ~K3bSoxEncoder(); + + QStringList extensions() const; + + QString fileTypeComment( const QString& ) const; + + long long fileSize( const QString&, const K3b::Msf& msf ) const; + + int pluginSystemVersion() const { return 3; } + + K3bPluginConfigWidget* createConfigWidget( QWidget* parent = 0, + const char* name = 0 ) const; + + /** + * reimplemented since sox writes the file itself + */ + bool openFile( const QString& ext, const QString& filename, const K3b::Msf& ); + void closeFile(); + + private slots: + void slotSoxFinished( KProcess* ); + void slotSoxOutputLine( const QString& ); + + private: + void finishEncoderInternal(); + bool initEncoderInternal( const QString& extension ); + long encodeInternal( const char* data, Q_ULONG len ); + + class Private; + Private* d; +}; + + +class K3bSoxEncoderSettingsWidget : public K3bPluginConfigWidget +{ + Q_OBJECT + + public: + K3bSoxEncoderSettingsWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bSoxEncoderSettingsWidget(); + + public slots: + void loadConfig(); + void saveConfig(); + + private: + base_K3bSoxEncoderConfigWidget* w; +}; + +#endif diff --git a/plugins/encoder/sox/k3bsoxencoder.plugin b/plugins/encoder/sox/k3bsoxencoder.plugin new file mode 100644 index 0000000..dd43f7d --- /dev/null +++ b/plugins/encoder/sox/k3bsoxencoder.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3bsoxencoder +Group=AudioEncoder +Name=K3b SoX Audio Encoder +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=2.0 +Comment=Encoding module to encode many file formats using SoX +License=GPL diff --git a/plugins/project/Makefile.am b/plugins/project/Makefile.am new file mode 100644 index 0000000..d762841 --- /dev/null +++ b/plugins/project/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = audioprojectcddb audiometainforenamer diff --git a/plugins/project/audiometainforenamer/Makefile.am b/plugins/project/audiometainforenamer/Makefile.am new file mode 100644 index 0000000..6199e50 --- /dev/null +++ b/plugins/project/audiometainforenamer/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core \ + -I$(srcdir)/../../../libk3b/core \ + -I$(srcdir)/../../../libk3b/plugin \ + -I$(srcdir)/../../../libk3b/tools \ + -I$(srcdir)/../../../libk3b/projects \ + -I$(srcdir)/../../../libk3b/projects/datacd \ + -I$(srcdir)/../../../libk3bdevice \ + $(all_includes) + +kde_module_LTLIBRARIES = libk3baudiometainforenamerplugin.la + +libk3baudiometainforenamerplugin_la_SOURCES = k3baudiometainforenamerplugin.cpp +libk3baudiometainforenamerplugin_la_LIBADD = ../../../libk3b/libk3b.la +libk3baudiometainforenamerplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3baudiometainforenamerplugin.plugin + +METASOURCES = AUTO diff --git a/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.cpp b/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.cpp new file mode 100644 index 0000000..1fb39a6 --- /dev/null +++ b/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.cpp @@ -0,0 +1,392 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3baudiometainforenamerplugin.h" + +// the k3b stuff we need +#include <k3bcore.h> +#include <k3bdatadoc.h> +#include <k3bdiritem.h> +#include <k3bfileitem.h> +#include <k3blistview.h> +#include <k3bpluginfactory.h> + +#include <kdebug.h> +#include <kinstance.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kfilemetainfo.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kmimetype.h> +#include <kdialog.h> + +#include <qstring.h> +#include <qfile.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qpushbutton.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <qpair.h> +#include <qvaluelist.h> +#include <qlayout.h> +#include <qptrdict.h> + + + +K_EXPORT_COMPONENT_FACTORY( libk3baudiometainforenamerplugin, K3bPluginFactory<K3bAudioMetainfoRenamerPlugin>("libk3baudiometainforenamerplugin") ) + + +class K3bAudioMetainfoRenamerPluginWidget::Private +{ +public: + K3bDataDoc* doc; + QString pattern; + + KComboBox* comboPattern; + K3bListView* viewFiles; + // KProgressDialog* progressDialog; + QPushButton* scanButton; + + QValueList< QPair<K3bFileItem*, QCheckListItem*> > renamableItems; + QPtrDict<QListViewItem> dirItemDict; + +// long long scannedSize; +// int progressCounter; +}; + + +K3bAudioMetainfoRenamerPluginWidget::K3bAudioMetainfoRenamerPluginWidget( K3bDoc* doc, + QWidget* parent, + const char* name ) + : QWidget( parent, name ) +{ + d = new Private(); + d->doc = dynamic_cast<K3bDataDoc*>(doc); + // d->progressDialog = 0; + + // pattern group + QGroupBox* patternGroup = new QGroupBox( 2, Qt::Horizontal, + i18n("Rename Pattern"), this ); + patternGroup->setInsideMargin( KDialog::marginHint() ); + patternGroup->setInsideSpacing( KDialog::spacingHint() ); + + d->comboPattern = new KComboBox( patternGroup ); + d->comboPattern->setEditable( true ); + + d->scanButton = new QPushButton( i18n("Scan"), patternGroup ); + + // the files view + QGroupBox* filesGroup = new QGroupBox( 1, Qt::Horizontal, + i18n("Found Files"), this ); + filesGroup->setInsideMargin( KDialog::marginHint() ); + filesGroup->setInsideSpacing( KDialog::spacingHint() ); + + d->viewFiles = new K3bListView( filesGroup ); + d->viewFiles->addColumn( i18n("New Name") ); + d->viewFiles->addColumn( i18n("Old Name") ); + d->viewFiles->setNoItemText( i18n("Please click the Scan button to search for renameable files.") ); + + // layout + QVBoxLayout* box = new QVBoxLayout( this ); + box->setMargin( 0 ); + box->setSpacing( KDialog::spacingHint() ); + + box->addWidget( patternGroup ); + box->addWidget( filesGroup ); + + connect( d->scanButton, SIGNAL(clicked()), this, SLOT(slotScanClicked()) ); + + QToolTip::add( d->scanButton, i18n("Scan for renamable files") ); + QWhatsThis::add( d->comboPattern, i18n("<qt>This specifies how the files should be renamed. " + "Currently only the special strings <em>%a</em> (Artist), " + "<em>%n</em> (Track number), and <em>%t</em> (Title) ," + "are supported.") ); +} + + +K3bAudioMetainfoRenamerPluginWidget::~K3bAudioMetainfoRenamerPluginWidget() +{ + delete d; +} + + +QString K3bAudioMetainfoRenamerPluginWidget::title() const +{ + return i18n("Rename Audio Files"); +} + + +QString K3bAudioMetainfoRenamerPluginWidget::subTitle() const +{ + return i18n("Based on meta info"); +} + + +void K3bAudioMetainfoRenamerPluginWidget::loadDefaults() +{ + d->comboPattern->setEditText( "%a - %t" ); +} + + +void K3bAudioMetainfoRenamerPluginWidget::readSettings( KConfigBase* c ) +{ + d->comboPattern->setEditText( c->readEntry( "rename pattern", "%a - %t" ) ); +} + + +void K3bAudioMetainfoRenamerPluginWidget::saveSettings( KConfigBase* c ) +{ + c->writeEntry( "rename pattern", d->comboPattern->currentText() ); +} + + +void K3bAudioMetainfoRenamerPluginWidget::slotScanClicked() +{ + d->pattern = d->comboPattern->currentText(); + if( d->pattern.isEmpty() ) { + KMessageBox::error( this, i18n("Please specify a valid pattern.") ); + } + else { +// if( d->progressDialog == 0 ) { +// d->progressDialog = new KProgressDialog( this, "scanning_progress", +// i18n("Scanning..."), +// i18n("Scanning for renameable files."), +// true ); +// d->progressDialog->setAllowCancel(false); +// } + + K3bDirItem* dir = d->doc->root(); + + // clear old searches + d->viewFiles->clear(); + d->renamableItems.clear(); + d->dirItemDict.clear(); +// d->scannedSize = 0; +// d->progressCounter = 0; + + // create root item + KListViewItem* rootItem = new KListViewItem( d->viewFiles, "/" ); + + // d->progressDialog->show(); + scanDir( dir, rootItem ); + // d->progressDialog->close(); + + rootItem->setOpen(true); + + if( d->renamableItems.isEmpty() ) + KMessageBox::sorry( this, i18n("No renameable files found.") ); + } +} + + +void K3bAudioMetainfoRenamerPluginWidget::scanDir( K3bDirItem* dir, QListViewItem* viewRoot ) +{ + kdDebug() << "(K3bAudioMetainfoRenamerPluginWidget) scanning dir " << dir->k3bName() << endl; + + d->dirItemDict.insert( dir, viewRoot ); + + for( QPtrListIterator<K3bDataItem> it( dir->children() ); it.current(); ++it ) { + K3bDataItem* item = it.current(); + + if( item->isFile() ) { + if( item->isRenameable() ) { + QString newName = createNewName( (K3bFileItem*)item ); + if( !newName.isEmpty() ) { + QCheckListItem* fileViewItem = new QCheckListItem( viewRoot, + newName, + QCheckListItem::CheckBox ); + fileViewItem->setText(1, item->k3bName() ); + fileViewItem->setOn(true); + d->renamableItems.append( qMakePair( (K3bFileItem*)item, fileViewItem ) ); + } + } + +// d->scannedSize += item->k3bSize(); +// d->progressCounter++; +// if( d->progressCounter > 50 ) { +// d->progressCounter = 0; +// d->progressDialog->progressBar()->setProgress( 100*d->scannedSize/d->doc->root()->k3bSize() ); +// qApp->processEvents(); +// } + } + else if( item->isDir() ) { + // create dir item + KListViewItem* dirViewItem = new KListViewItem( viewRoot, item->k3bName() ); + scanDir( (K3bDirItem*)item, dirViewItem ); + dirViewItem->setOpen(true); + } + } +} + + +void K3bAudioMetainfoRenamerPluginWidget::activate() +{ + if( d->renamableItems.isEmpty() ) { + KMessageBox::sorry( this, i18n("Please click the Scan button to search for renameable files.") ); + } + else { + for( QValueList< QPair<K3bFileItem*, QCheckListItem*> >::iterator it = d->renamableItems.begin(); + it != d->renamableItems.end(); ++it ) { + QPair<K3bFileItem*, QCheckListItem*>& item = *it; + + if( item.second->isOn() ) + item.first->setK3bName( item.second->text(0) ); + } + + d->viewFiles->clear(); + d->renamableItems.clear(); + + KMessageBox::information( this, i18n("Done.") ); + } +} + + +QString K3bAudioMetainfoRenamerPluginWidget::createNewName( K3bFileItem* item ) +{ + KMimeType::Ptr mimetype = KMimeType::findByPath( item->localPath() ); + // sometimes ogg-vorbis files go as "application/x-ogg" + if( mimetype != 0 && + ( mimetype->name().contains( "audio" ) || mimetype->name().contains("ogg") ) ) { + + QString artist, title, track; + + KFileMetaInfo metaInfo( item->localPath() ); + if( metaInfo.isValid() ) { + + KFileMetaInfoItem artistItem = metaInfo.item( "Artist" ); + KFileMetaInfoItem titleItem = metaInfo.item( "Title" ); + KFileMetaInfoItem trackItem = metaInfo.item( "Tracknumber" ); + + if( artistItem.isValid() ) + artist = artistItem.string().stripWhiteSpace(); + + if( titleItem.isValid() ) + title = titleItem.string().stripWhiteSpace(); + + if( trackItem.isValid() ) + track = track.sprintf("%02d",trackItem.string().toInt()); + } + + QString newName; + for( unsigned int i = 0; i < d->pattern.length(); ++i ) { + + if( d->pattern[i] == '%' ) { + ++i; + + if( i < d->pattern.length() ) { + if( d->pattern[i] == 'a' ) { + if( artist.isEmpty() ) + return QString::null; + newName.append(artist); + } + else if( d->pattern[i] == 'n' ) { + if( title.isEmpty() ) + return QString::null; + newName.append(track); + } + else if( d->pattern[i] == 't' ) { + if( title.isEmpty() ) + return QString::null; + newName.append(title); + } + else { + newName.append( "%" ); + newName.append( d->pattern[i] ); + } + } + else { // end of pattern + newName.append( "%" ); + } + } + else { + newName.append( d->pattern[i] ); + } + } + + // remove white spaces from end and beginning + newName = newName.stripWhiteSpace(); + + QString extension = item->k3bName().mid( item->k3bName().findRev(".") ); + + if( !newName.isEmpty() ) { + // + // Check if files with that name exists and if so append number + // + if( existsOtherItemWithSameName( item, newName + extension ) ) { + kdDebug() << "(K3bAudioMetainfoRenamerPluginWidget) file with name " + << newName << extension << " already exists" << endl; + int i = 1; + while( existsOtherItemWithSameName( item, newName + QString( " (%1)").arg(i) + extension ) ) + i++; + newName.append( QString( " (%1)").arg(i) ); + } + + // append extension + newName.append( extension ); + } + + return newName; + } + else + return QString::null; +} + + +bool K3bAudioMetainfoRenamerPluginWidget::existsOtherItemWithSameName( K3bFileItem* item, const QString& name ) +{ + K3bDirItem* dir = item->parent(); + K3bDataItem* otherItem = dir->find( name ); + if( otherItem && otherItem != item ) + return true; + + QListViewItem* dirViewItem = d->dirItemDict[dir]; + QListViewItem* current = dirViewItem->firstChild(); + while( current && current->parent() == dirViewItem ) { + if( current->text(0) == name ) + return true; + current = current->nextSibling(); + } + + return false; +} + + + +K3bAudioMetainfoRenamerPlugin::K3bAudioMetainfoRenamerPlugin( QObject* parent, + const char* name ) + : K3bProjectPlugin( DATA_PROJECTS, true, parent, name ) +{ + setText( i18n("Rename Audio Files") ); + setToolTip( i18n("Rename audio files based on their meta info.") ); +} + + +K3bAudioMetainfoRenamerPlugin::~K3bAudioMetainfoRenamerPlugin() +{ +} + + +K3bProjectPluginGUIBase* K3bAudioMetainfoRenamerPlugin::createGUI( K3bDoc* doc, QWidget* parent, const char* name ) +{ + return new K3bAudioMetainfoRenamerPluginWidget( doc, parent, name ); +} + +#include "k3baudiometainforenamerplugin.moc" diff --git a/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.h b/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.h new file mode 100644 index 0000000..b387aa5 --- /dev/null +++ b/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.h @@ -0,0 +1,76 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_AUDIO_METAINFO_RENAMER_PLUGIN_H_ +#define _K3B_AUDIO_METAINFO_RENAMER_PLUGIN_H_ + + +#include <k3bprojectplugin.h> +#include <qwidget.h> + + +class K3bDataDoc; +class K3bDirItem; +class K3bFileItem; +class QListViewItem; + + +class K3bAudioMetainfoRenamerPluginWidget : public QWidget, public K3bProjectPluginGUIBase +{ + Q_OBJECT + + public: + K3bAudioMetainfoRenamerPluginWidget( K3bDoc* doc, QWidget* parent = 0, const char* name = 0 ); + ~K3bAudioMetainfoRenamerPluginWidget(); + + QWidget* qWidget() { return this; } + + QString title() const; + QString subTitle() const; + + void loadDefaults(); + void readSettings( KConfigBase* ); + void saveSettings( KConfigBase* ); + + void activate(); + + private slots: + void slotScanClicked(); + + private: + void scanDir( K3bDirItem*, QListViewItem* parent ); + QString createNewName( K3bFileItem* ); + bool existsOtherItemWithSameName( K3bFileItem*, const QString& ); + + class Private; + Private* d; +}; + + +class K3bAudioMetainfoRenamerPlugin : public K3bProjectPlugin +{ + Q_OBJECT + + public: + K3bAudioMetainfoRenamerPlugin( QObject* parent, const char* name ); + ~K3bAudioMetainfoRenamerPlugin(); + + int pluginSystemVersion() const { return 3; } + + K3bProjectPluginGUIBase* createGUI( K3bDoc*, QWidget* = 0, const char* = 0 ); +}; + + +#endif diff --git a/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.plugin b/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.plugin new file mode 100644 index 0000000..42c24d8 --- /dev/null +++ b/plugins/project/audiometainforenamer/k3baudiometainforenamerplugin.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3baudiometainforenamerplugin +Group=ProjectPlugin +Name=K3b Audio Metainfo Renamer +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=1.0 +Comment=Plugin to rename audio files in a data project based on the meta info. +License=GPL diff --git a/plugins/project/audioprojectcddb/Makefile.am b/plugins/project/audioprojectcddb/Makefile.am new file mode 100644 index 0000000..8af3e67 --- /dev/null +++ b/plugins/project/audioprojectcddb/Makefile.am @@ -0,0 +1,22 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core \ + -I$(srcdir)/../../../libk3b/tools \ + -I$(srcdir)/../../../libk3b/core \ + -I$(srcdir)/../../../libk3b/projects/audiocd \ + -I$(srcdir)/../../../libk3b/projects \ + -I$(srcdir)/../../../libk3bdevice \ + -I$(srcdir)/../../../libk3b/cddb \ + -I$(srcdir)/../../../libk3b/plugin \ + $(all_includes) + +kde_module_LTLIBRARIES = libk3baudioprojectcddbplugin.la + +libk3baudioprojectcddbplugin_la_SOURCES = k3baudioprojectcddbplugin.cpp + +libk3baudioprojectcddbplugin_la_LIBADD = ../../../libk3b/libk3b.la + +libk3baudioprojectcddbplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) + +pluginsdir = $(kde_datadir)/k3b/plugins +plugins_DATA = k3baudioprojectcddbplugin.plugin + +METASOURCES = AUTO diff --git a/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.cpp b/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.cpp new file mode 100644 index 0000000..e523eb3 --- /dev/null +++ b/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.cpp @@ -0,0 +1,151 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> + +#include "k3baudioprojectcddbplugin.h" + +// the k3b stuff we need +#include <k3bcore.h> +#include <k3bglobals.h> +#include <k3baudiodoc.h> +#include <k3baudiotrack.h> +#include <k3btoc.h> +#include <k3btrack.h> +#include <k3bmsf.h> +#include <k3bcddb.h> +#include <k3bcddbresult.h> +#include <k3bcddbquery.h> +#include <k3bprogressdialog.h> +#include <k3bpluginfactory.h> + +#include <kdebug.h> +#include <kaction.h> +#include <kinstance.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kconfig.h> + +#include <qstring.h> + + +K_EXPORT_COMPONENT_FACTORY( libk3baudioprojectcddbplugin, K3bPluginFactory<K3bAudioProjectCddbPlugin>( "libk3baudioprojectcddbplugin" ) ) + + +K3bAudioProjectCddbPlugin::K3bAudioProjectCddbPlugin( QObject* parent, + const char* name ) + : K3bProjectPlugin( AUDIO_CD, false, parent, name ), + m_cddb(0), + m_progress(0) +{ + setText( i18n("Query Cddb") ); + setToolTip( i18n("Query a cddb entry for the current audio project.") ); +} + + +K3bAudioProjectCddbPlugin::~K3bAudioProjectCddbPlugin() +{ + delete m_progress; +} + + +void K3bAudioProjectCddbPlugin::activate( K3bDoc* doc, QWidget* parent ) +{ + m_doc = dynamic_cast<K3bAudioDoc*>( doc ); + m_parentWidget = parent; + m_canceled = false; + + if( !m_doc || m_doc->numOfTracks() == 0 ) { + KMessageBox::sorry( parent, i18n("Please select a non-empty audio project for a cddb query.") ); + } + else { + if( !m_cddb ) { + m_cddb = new K3bCddb( this ); + connect( m_cddb, SIGNAL(queryFinished(int)), + this, SLOT(slotCddbQueryFinished(int)) ); + } + if( !m_progress ) { + m_progress = new K3bProgressDialog( i18n("Query Cddb"), parent, i18n("Audio Project") ); + connect( m_progress, SIGNAL(cancelClicked()), + this, SLOT(slotCancelClicked()) ); + } + + // read the k3b config :) + KConfig* c = k3bcore->config(); + c->setGroup("Cddb"); + m_cddb->readConfig( c ); + + // start the query + m_cddb->query( m_doc->toToc() ); + + m_progress->exec(false); + } +} + + +void K3bAudioProjectCddbPlugin::slotCancelClicked() +{ + m_canceled = true; + m_progress->close(); +} + + +void K3bAudioProjectCddbPlugin::slotCddbQueryFinished( int error ) +{ + if( !m_canceled ) { + m_progress->hide(); + + if( error == K3bCddbQuery::SUCCESS ) { + K3bCddbResultEntry cddbInfo = m_cddb->result(); + + // save the entry locally + KConfig* c = k3bcore->config(); + c->setGroup( "Cddb" ); + if( c->readBoolEntry( "save cddb entries locally", true ) ) + m_cddb->saveEntry( cddbInfo ); + + // save the entry to the doc + m_doc->setTitle( cddbInfo.cdTitle ); + m_doc->setPerformer( cddbInfo.cdArtist ); + m_doc->setCdTextMessage( cddbInfo.cdExtInfo ); + + int i = 0; + for( K3bAudioTrack* track = m_doc->firstTrack(); track; track = track->next() ) { + track->setTitle( cddbInfo.titles[i] ); + track->setPerformer( cddbInfo.artists[i] ); + track->setCdTextMessage( cddbInfo.extInfos[i] ); + + ++i; + } + + // and enable cd-text + m_doc->writeCdText( true ); + } + else if( error == K3bCddbQuery::NO_ENTRY_FOUND ) { + KMessageBox::information( m_parentWidget, i18n("No CDDB entry found."), i18n("CDDB") ); + } + else if( error != K3bCddbQuery::CANCELED ) { + KMessageBox::information( m_parentWidget, m_cddb->errorString(), i18n("Cddb error") ); + } + } + + // make sure the progress dialog does not get deleted by it's parent + delete m_progress; + m_doc = 0; + m_parentWidget = 0; + m_progress = 0; +} + +#include "k3baudioprojectcddbplugin.moc" diff --git a/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.h b/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.h new file mode 100644 index 0000000..a0e7880 --- /dev/null +++ b/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.h @@ -0,0 +1,53 @@ +/* + * + * $Id$ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_AUDIO_PROJECT_CDDB_PLUGIN_H_ +#define _K3B_AUDIO_PROJECT_CDDB_PLUGIN_H_ + + +#include <k3bprojectplugin.h> + +class K3bCddb; +class K3bAudioDoc; +class K3bProgressDialog; +class QWidget; + +class K3bAudioProjectCddbPlugin : public K3bProjectPlugin +{ + Q_OBJECT + + public: + K3bAudioProjectCddbPlugin( QObject* parent, const char* name ); + ~K3bAudioProjectCddbPlugin(); + + int pluginSystemVersion() const { return 3; } + + void activate( K3bDoc* doc, QWidget* parent ); + + private slots: + void slotCddbQueryFinished( int result ); + void slotCancelClicked(); + + private: + K3bCddb* m_cddb; + K3bAudioDoc* m_doc; + K3bProgressDialog* m_progress; + QWidget* m_parentWidget; + + bool m_canceled; +}; + + +#endif diff --git a/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.plugin b/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.plugin new file mode 100644 index 0000000..8fbea0f --- /dev/null +++ b/plugins/project/audioprojectcddb/k3baudioprojectcddbplugin.plugin @@ -0,0 +1,9 @@ +[K3b Plugin] +Lib=libk3baudioprojectcddbplugin +Group=ProjectPlugin +Name=K3b Cddb Audio Plugin +Author=Sebastian Trueg +Email=trueg@k3b.org +Version=0.1 +Comment=Plugin to query a cddb server for information about an audio project. +License=GPL |