diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-03 02:15:56 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-03 02:15:56 +0000 |
commit | 50b48aec6ddd451a6d1709c0942477b503457663 (patch) | |
tree | a9ece53ec06fd0a2819de7a2a6de997193566626 /plugins/decoder | |
download | k3b-50b48aec6ddd451a6d1709c0942477b503457663.tar.gz k3b-50b48aec6ddd451a6d1709c0942477b503457663.zip |
Added abandoned KDE3 version of K3B
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k3b@1084400 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'plugins/decoder')
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 |