Commit cdca8aab authored by Benjamin Meyer's avatar Benjamin Meyer
Browse files

Audiocd Code Refactoring commit

CCMAIL: kde-core-devel@kde.org
-------------
The commit you all (ok, maybe just me and a few others) have been waiting for.

audiocd:/ no longer uses ifdefs!!!

There is a new base class called Encoder which the different encoders are
based off of.  Wav is in fact based upon CDA which is based upon Encoder.

This means that the audiocd.h and audiocd.cpp files only deal with
ripping the cd and with the ioslave work and just make a call to the
selected encoder to do the work, no more case, ifdef, if else else else else...

In the process of converting the module I discovered countless edge
cases bugs that a) never were reported, b) probably would be too hard to find
if you didn't know where to look. c) most arn't noticable.  By simply having
the one call to the base class rather then the bunch of ifdefs acidents in
copy/paste were all removed.

The work that I did was 99% just refactoring of the code.  Very little new
code was added to the project.  The majority of the actuall code changes were bug fixes.  Taking this approch first and then enhancing it I thought would be best.  In fact will all the fixes it might be worth while to put in the 3_2
tree.

Future work(?)
--------------
1) Currently the Encoder are build into the audiocd binary, but one could
easily make them into libraries.  This offers the really neat idea that
one could re-use these in other applications (KAudioCreator, Juk, etc).
Maybe not re-using the encoding portion, but simply to get the configure
dialog for that encoder so that the third party application could use it
and then just make an audiocd ioslave call to get the file after saving
the settings.

2) Now that the Encoding is abstracted maybe doing the same with the
ripper would be a good idea.  This would allow for a cdda2wav module.

3) With the Encoder class it is 1000% times easier to add a new encoders
to the audiocd framework.  (Anyone feel free to write a Flac encoder!)

A far as the user is concerned it should be very similar.  I think the only
difference is that there is now a CDA and a Wav directory for the two encoders.  I am very happy with how smoothly this went and hope it will encurage others
to take a look into working on this module.

P.S. TODO the lame encoder doesn't set the year (never did) in the id3 tag.

svn path=/trunk/kdemultimedia/kioslave/audiocd/; revision=285992
parent 73e7cfef
......@@ -6,14 +6,14 @@ INCLUDES = $(CDPARANOIA_INCS) $(LAME_INCS) $(VORBIS_INCS) -I$(top_srcdir)/libkcd
kde_module_LTLIBRARIES = kio_audiocd.la
kio_audiocd_la_SOURCES = audiocd.cpp
kio_audiocd_la_SOURCES = audiocd.cpp encoderlame.cpp encodervorbis.cpp encoderwav.cpp encodercda.cpp
kio_audiocd_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(CDPARANOIA_RPATH) \
$(LAME_RPATH) $(VORBIS_RPATH) -module $(KDE_PLUGIN)
kio_audiocd_la_LIBADD = $(LIB_KIO) $(CDPARANOIA_LIBS) $(VORBISENC_LIBS) $(VORBIS_LIBS) $(top_builddir)/libkcddb/libkcddb.la
noinst_HEADERS = audiocd.h
noinst_HEADERS = audiocd.h encoderlame.h encodervorbis.h encoderwav.h encodercda.h
protocoldir = $(kde_servicesdir)
protocol_DATA = audiocd.protocol
......
This diff is collapsed.
......@@ -21,6 +21,13 @@
#ifndef AUDIO_CD_H
#define AUDIO_CD_H
#include <qmap.h>
#include "encoder.h"
class EncoderLame;
class EncoderVorbis;
class EncoderWav;
class EncoderCda;
#include <qstring.h>
#include <kio/global.h>
......@@ -31,6 +38,11 @@ struct cdrom_drive;
namespace AudioCD {
/**
* The core class of the audiocd:// ioslave.
* It has the iosalve login and the ripping logic. The actual encoding
* is done by encoders that are seperate objects.
*/
class AudioCDProtocol : public KIO::SlaveBase
{
public:
......@@ -66,11 +78,10 @@ class AudioCDProtocol : public KIO::SlaveBase
DirTypeVorbis
};
static QString extension(enum FileType fileType);
static FileType fileTypeFromExtension(const QString& extension);
static FileType determineFiletype(const QString & filename);
QString extension(enum FileType fileType);
FileType fileTypeFromExtension(const QString& extension);
FileType determineFiletype(const QString & filename);
void writeHeader(long);
struct cdrom_drive * findDrive(bool &noPermission);
void parseArgs(const KURL &);
......@@ -126,14 +137,16 @@ class AudioCDProtocol : public KIO::SlaveBase
Private * d;
private:
cdrom_drive * pickDrive();
unsigned int get_discid(cdrom_drive *);
KLibrary *_lamelib;
bool initLameLib();
QMap<FileType, Encoder*> encoders;
Encoder *lame;
#ifdef HAVE_VORBIS
long vorbisSize(long time_secs);
long flush_vorbis(void);
Encoder *vorbis;
#endif
Encoder *wav;
Encoder *cda;
cdrom_drive * pickDrive();
unsigned int get_discid(cdrom_drive *);
};
} // end namespace AudioCD
......
/*
Copyright (C) 2004 Benjamin Meyer <ben + audiocd at meyerhome dot net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef AUDIOCD_ENCODER_H
#define AUDIOCD_ENCODER_H
#include <sys/types.h>
#include <kio/slavebase.h>
class KConfig;
class Encoder {
public:
/**
* Constructor.
* @param slave parent that this classes can use to call data() with
* when finished encoding bits.
*/
Encoder(KIO::SlaveBase *slave) : ioslave(slave) {};
/**
* Deconstructor.
*/
virtual ~Encoder(){};
/**
* Initiallizes the decoder, loading libraries, etc. Encoders
* that don't return true will will deleted and not used.
* @returns false if unable to initialize the encoder.
*/
virtual bool init() = 0;
/**
* The encoder should read in its config data here.
* When possible using the type() for the group is best.
* config is shared with all plugins so stay in your own
* group.
* @param config pointer to active KConfig object.
*/
virtual void getParameters(KConfig *config) = 0;
/**
* Helper function to determine the end size of a
* encoded file.
* @param time_secs the lengh of the audio track in seconds.
* @returns the size of a file if it is time_secs in length.
*/
virtual unsigned long size(long time_secs) = 0;
/**
* @returns the generic user string type/name of this encoder
* Examples: "MP3", "Ogg Vorbis", "Wav", "FID Level 2", etc
*/
virtual QString type() = 0;
/**
* @returns the mime type for the files this encoder produces.
* Example: "audio/x-wav"
*/
virtual const char * mimeType() = 0;
/**
* @returns the file type for the files this encoder produces.
* Used in naming of the file for example foo.mp3
* Examples: "mp3", "ogg", "wav"
*/
virtual const char * fileType() = 0;
/**
* Before the read functions are called this is
* called to allow the encoders to store the cddb
* information if they want to so it can be inserted
* where neccessary (start, middle, end, or combos etc).
*/
virtual void fillSongInfo(QString trackName,
QString cdArtist,
QString cdTitle,
QString cdCategory,
int trackNumber,
int cdYear) = 0;
/**
* Perform any initial file creation necessary for a new song (that
* has just been sent via fillSongInfo())
* @param size - the total binary size of the end file (via size()).
* @return size of the data that was created by this function.
*/
virtual long readInit(long size) = 0;
/**
* Passes a little bit of cd data to be encoded
* This function is most likly called many many times.
* @param buf pointer to the audio that has been read in so far
* @param frames the number of frames of audio that are in buf
* @return size of the data that was created by this function.
*/
virtual long read(int16_t * buf, int frames) = 0;
/**
* Perform any final file creation/padding that is necessary
* @return size of the data that was created by this function.
*/
virtual long readCleanup() = 0;
protected:
/**
* Pointer to the ioslave that is running this encoder.
* Used (only?) for the data() function to pass back encoded data.
*/
KIO::SlaveBase *ioslave;
};
#endif // AUDIOCD_ENCODER_H
/*
Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
Copyright (C) 2004 Benjamin Meyer <ben + audiocd at meyerhoem dot net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "encodercda.h"
class EncoderCda::Private
{
public:
};
unsigned long EncoderCda::size(long time_secs){
//return (time_secs * (44100 * 2 * 16))/8;
return (time_secs) * 176400;
}
const char * EncoderCda::mimeType(){
return "audio/x-cda";
}
// Remove this by calculating CD_FRAMESIZE_RAW from the frames
extern "C"
{
#include <cdda_interface.h>
}
inline int16_t swap16 (int16_t i)
{
return (((i >> 8) & 0xFF) | ((i << 8) & 0xFF00));
}
long EncoderCda::read(int16_t * buf, int frames){
QByteArray output;
int16_t i16 = 1;
/* WAV is defined to be little endian, so we need to swap it
on big endian platforms. */
if (((char*)&i16)[0] == 0)
for (int i=0; i < 2 * frames; i++)
buf[i] = swap16 (buf[i]);
char * cbuf = reinterpret_cast<char *>(buf);
output.setRawData(cbuf, CD_FRAMESIZE_RAW);
ioslave->data(output);
output.resetRawData(cbuf, CD_FRAMESIZE_RAW);
return CD_FRAMESIZE_RAW;
};
/*
Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
Copyright (C) 2004 Benjamin Meyer <ben + audiocd at meyerhoem dot net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ENCODER_CDA_H
#define ENCODER_CDA_H
#include "encoder.h"
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
/**
* Raw cd "encoder"
* Does little more then copy the data and make sure it is in the right
* endian.
*/
class EncoderCda : public Encoder {
public:
EncoderCda(KIO::SlaveBase *slave) : Encoder(slave) {};
~EncoderCda(){};
virtual bool init(){ return true; };
virtual void getParameters(KConfig *){};
virtual unsigned long size(long time_secs);
virtual QString type(){ return "CDA"; };
virtual const char * mimeType();
virtual const char * fileType(){ return "cda"; };
virtual void fillSongInfo(QString,
QString,
QString,
QString,
int,
int){};
virtual long readInit(long){ return 0; };
virtual long read(int16_t * buf, int frames);
virtual long readCleanup(){ return 0; };
private:
class Private;
Private * d;
};
#endif // ENCODER_CDA_H
/*
Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
Copyright (C) 2000, 2001, 2002 Michael Matz <matz@kde.org>
Copyright (C) 2001 Carsten Duvenhorst <duvenhorst@m2.uni-hannover.de>
Copyright (C) 2001 Adrian Schroeter <adrian@suse.de>
Copyright (C) 2003 Richard Lärkäng <richard@goteborg.utfors.se>
Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
Copyright (C) 2004 Benjamin Meyer <ben + audiocd at meyerhoem dot net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include "encoderlame.h"
#include <klibloader.h>
#include <kconfig.h>
#include <kdebug.h>
#ifdef HAVE_LAME
#include <lame/lame.h>
#else
/*
* These are copied from lame.h and should allow lame support to be compiled in
* even if the headers are not available. This was requested by the Debian
* packagers so that this can be compiled in and be purely a runtime dependancy.
*/
struct lame_global_flags;
typedef lame_global_flags *lame_t;
typedef enum vbr_mode_e {
vbr_off=0,
vbr_mt,
vbr_rh,
vbr_abr,
vbr_mtrh,
vbr_max_indicator,
vbr_default=vbr_rh
} vbr_mode;
typedef enum MPEG_mode_e {
STEREO = 0,
JOINT_STEREO,
DUAL_CHANNEL,
MONO,
NOT_SET,
MAX_INDICATOR
} MPEG_mode;
#endif
#define QFL1(x) QString::fromLatin1(x)
extern "C"
{
static int _lamelibMissing = false;
static lame_global_flags* (*_lamelib_lame_init)(void) = NULL;
static int (*_lamelib_lame_init_params) (lame_global_flags*) = NULL;
static void (*_lamelib_id3tag_init)(lame_global_flags*) = NULL;
static void (*_lamelib_id3tag_set_album)(lame_global_flags*, const char*) = NULL;
static void (*_lamelib_id3tag_set_artist)(lame_global_flags*, const char*) = NULL;
static void (*_lamelib_id3tag_set_title)(lame_global_flags*, const char*) = NULL;
static void (*_lamelib_id3tag_set_track)(lame_global_flags*, const char*) = NULL;
static int (*_lamelib_lame_encode_buffer_interleaved) (
lame_global_flags*, short int*, int, unsigned char*, int) = NULL;
static int (*_lamelib_lame_encode_finish) (
lame_global_flags*, unsigned char*, int ) = NULL;
static int (*_lamelib_lame_set_VBR) ( lame_global_flags*, vbr_mode ) = NULL;
static int (*_lamelib_lame_get_VBR) ( lame_global_flags* ) = NULL;
static int (*_lamelib_lame_set_brate) ( lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_get_brate) ( lame_global_flags* ) = NULL;
static int (*_lamelib_lame_set_quality) ( lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_VBR_mean_bitrate_kbps) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_get_VBR_mean_bitrate_kbps) (
lame_global_flags* ) = NULL;
static int (*_lamelib_lame_set_VBR_min_bitrate_kbps) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_VBR_hard_min) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_VBR_max_bitrate_kbps) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_VBR_q) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_bWriteVbrTag) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_mode) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_copyright) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_original) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_strict_ISO) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_error_protection) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_lowpassfreq) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_lowpasswidth) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_highpassfreq) (
lame_global_flags*, int ) = NULL;
static int (*_lamelib_lame_set_highpasswidth) (
lame_global_flags*, int ) = NULL;
}
class EncoderLame::Private
{
public:
lame_global_flags *gf;
int bitrate;
bool write_id3;
};
EncoderLame::EncoderLame(KIO::SlaveBase *slave) : Encoder(slave) {
d = new Private();
}
EncoderLame::~EncoderLame(){
delete d;
}
bool EncoderLame::init(){
if ( _lamelib_lame_init != NULL )
return true;
if ( _lamelibMissing ) // we tried already, do not try again
return false;
// load the lame lib, if not done already
KLibLoader *LameLib = KLibLoader::self();
#ifdef __OpenBSD__
{
QString libname = findMostRecentLib("/usr/local/lib", "mp3lame");
if (!libname.isNull())
_lamelib = LameLib->globalLibrary(libname.latin1());
}
#else
QStringList libpaths, libnames;
libpaths << QFL1("/usr/lib/")
<< QFL1("/usr/local/lib/")
<< QString::null;
libnames << QFL1("libmp3lame.so.0")
<< QFL1("libmp3lame.so.0.0.0")
<< QFL1("libmp3lame.so");
for (QStringList::Iterator it = libpaths.begin();
it != libpaths.end();
++it) {
for (QStringList::Iterator lit = libnames.begin();
lit != libnames.end();
++lit) {
QString alib = *it+*lit;
_lamelib = LameLib->globalLibrary(alib.latin1());
if (_lamelib) break;
}
if (_lamelib) break;
}
#endif
if ( _lamelib == NULL ){
_lamelibMissing = true;
return false;
}else{
_lamelib_lame_init =
(lame_t (*) (void))
_lamelib->symbol("lame_init");
_lamelib_id3tag_init =
(void (*) (lame_global_flags*))
_lamelib->symbol("id3tag_init");
_lamelib_id3tag_set_album =
(void (*) (lame_global_flags*, const char*))
_lamelib->symbol("id3tag_set_album");
_lamelib_id3tag_set_artist =
(void (*) (lame_global_flags*, const char*))
_lamelib->symbol("id3tag_set_artist");
_lamelib_id3tag_set_title =
(void (*) (lame_global_flags*, const char*))
_lamelib->symbol("id3tag_set_title");
_lamelib_id3tag_set_track =
(void (*) (lame_global_flags*, const char*))
_lamelib->symbol("id3tag_set_track");
_lamelib_lame_init_params =
(int (*) (lame_global_flags*))
_lamelib->symbol("lame_init_params");
_lamelib_lame_encode_buffer_interleaved =
(int (*) (
lame_global_flags*, short int*, int, unsigned char*, int))
_lamelib->symbol("lame_encode_buffer_interleaved");
_lamelib_lame_encode_finish =
(int (*) (lame_global_flags*, unsigned char*, int))
_lamelib->symbol("lame_encode_finish");
_lamelib_lame_set_VBR =
(int (*) (lame_global_flags*, vbr_mode))
_lamelib->symbol("lame_set_VBR");
_lamelib_lame_get_VBR =
(int (*) (lame_global_flags*))
_lamelib->symbol("lame_get_VBR");
_lamelib_lame_set_brate =
(int (*) (lame_global_flags*, int))
_lamelib->symbol("lame_set_brate");
_lamelib_lame_get_brate =
(int (*) (lame_global_flags*))
_lamelib->symbol("lame_get_brate");
_lamelib_lame_set_quality =
(int (*) (lame_global_flags*, int))
_lamelib->symbol("lame_set_quality");
_lamelib_lame_set_VBR_mean_bitrate_kbps =
(int (*) (lame_global_flags*, int))
_lamelib->symbol("lame_set_VBR_mean_bitrate_kbps");
_lamelib_lame_get_VBR_mean_bitrate_kbps =
(int (*) ( lame_global_flags*))
_lamelib->symbol("lame_get_VBR_mean_bitrate_kbps");
_lamelib_lame_set_VBR_min_bitrate_kbps =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_VBR_min_bitrate_kbps");
_lamelib_lame_set_VBR_hard_min =
(int (*) (lame_global_flags*, int))
_lamelib->symbol("lame_set_VBR_hard_min");
_lamelib_lame_set_VBR_max_bitrate_kbps =
(int (*) (
lame_global_flags*, int))
_lamelib->symbol("lame_set_VBR_max_bitrate_kbps");
_lamelib_lame_set_VBR_q =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_VBR_q");
_lamelib_lame_set_bWriteVbrTag =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_bWriteVbrTag");
_lamelib_lame_set_mode =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_mode");
_lamelib_lame_set_copyright =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_copyright");
_lamelib_lame_set_original =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_original");
_lamelib_lame_set_strict_ISO =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_strict_ISO");
_lamelib_lame_set_error_protection =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_error_protection");
_lamelib_lame_set_lowpassfreq =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_lowpassfreq");
_lamelib_lame_set_lowpasswidth =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_lowpasswidth");
_lamelib_lame_set_highpassfreq =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_highpassfreq");
_lamelib_lame_set_highpasswidth =
(int (*) ( lame_global_flags*, int))
_lamelib->symbol("lame_set_highpasswidth");
// protecting for a crash in case of older lame lib
if ( _lamelib_lame_init == NULL || _lamelib_id3tag_init == NULL ||
_lamelib_id3tag_set_album == NULL ||
_lamelib_id3tag_set_artist == NULL ||
_lamelib_id3tag_set_title == NULL ||
_lamelib_id3tag_set_track == NULL ||
_lamelib_lame_init_params == NULL ||