summaryrefslogtreecommitdiffstats
path: root/plugins/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/decoder')
-rw-r--r--plugins/decoder/Makefile.am26
-rw-r--r--plugins/decoder/ffmpeg/Makefile.am15
-rw-r--r--plugins/decoder/ffmpeg/configure.in.bot25
-rw-r--r--plugins/decoder/ffmpeg/configure.in.in68
-rw-r--r--plugins/decoder/ffmpeg/k3bffmpegdecoder.cpp155
-rw-r--r--plugins/decoder/ffmpeg/k3bffmpegdecoder.h67
-rw-r--r--plugins/decoder/ffmpeg/k3bffmpegdecoder.plugin9
-rw-r--r--plugins/decoder/ffmpeg/k3bffmpegwrapper.cpp375
-rw-r--r--plugins/decoder/ffmpeg/k3bffmpegwrapper.h85
-rw-r--r--plugins/decoder/flac/Makefile.am13
-rw-r--r--plugins/decoder/flac/configure.in.bot13
-rw-r--r--plugins/decoder/flac/configure.in.in41
-rw-r--r--plugins/decoder/flac/k3bflacdecoder.cpp494
-rw-r--r--plugins/decoder/flac/k3bflacdecoder.h68
-rw-r--r--plugins/decoder/flac/k3bflacdecoder.plugin9
-rw-r--r--plugins/decoder/libsndfile/Makefile.am14
-rw-r--r--plugins/decoder/libsndfile/configure.in.bot11
-rw-r--r--plugins/decoder/libsndfile/configure.in.in52
-rw-r--r--plugins/decoder/libsndfile/k3blibsndfiledecoder.cpp254
-rw-r--r--plugins/decoder/libsndfile/k3blibsndfiledecoder.h69
-rw-r--r--plugins/decoder/libsndfile/k3blibsndfiledecoder.plugin9
-rw-r--r--plugins/decoder/mp3/Makefile.am13
-rw-r--r--plugins/decoder/mp3/configure.in.bot11
-rw-r--r--plugins/decoder/mp3/configure.in.in24
-rw-r--r--plugins/decoder/mp3/k3bmad.cpp359
-rw-r--r--plugins/decoder/mp3/k3bmad.h92
-rw-r--r--plugins/decoder/mp3/k3bmaddecoder.cpp542
-rw-r--r--plugins/decoder/mp3/k3bmaddecoder.h79
-rw-r--r--plugins/decoder/mp3/k3bmaddecoder.plugin9
-rw-r--r--plugins/decoder/musepack/Makefile.am16
-rw-r--r--plugins/decoder/musepack/configure.in.bot12
-rw-r--r--plugins/decoder/musepack/configure.in.in52
-rw-r--r--plugins/decoder/musepack/k3bmpcdecoder.cpp111
-rw-r--r--plugins/decoder/musepack/k3bmpcdecoder.h62
-rw-r--r--plugins/decoder/musepack/k3bmpcdecoder.plugin9
-rw-r--r--plugins/decoder/musepack/k3bmpcwrapper.cpp193
-rw-r--r--plugins/decoder/musepack/k3bmpcwrapper.h58
-rw-r--r--plugins/decoder/ogg/Makefile.am13
-rw-r--r--plugins/decoder/ogg/configure.in.bot11
-rw-r--r--plugins/decoder/ogg/configure.in.in32
-rw-r--r--plugins/decoder/ogg/k3boggvorbisdecoder.cpp252
-rw-r--r--plugins/decoder/ogg/k3boggvorbisdecoder.h72
-rw-r--r--plugins/decoder/ogg/k3boggvorbisdecoder.plugin9
-rw-r--r--plugins/decoder/skeleton.cpp101
-rw-r--r--plugins/decoder/skeleton.h57
-rw-r--r--plugins/decoder/skeleton.plugin9
-rw-r--r--plugins/decoder/wave/Makefile.am13
-rw-r--r--plugins/decoder/wave/k3bwavedecoder.cpp392
-rw-r--r--plugins/decoder/wave/k3bwavedecoder.h74
-rw-r--r--plugins/decoder/wave/k3bwavedecoder.plugin9
50 files changed, 4558 insertions, 0 deletions
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