From e2de64d6f1beb9e492daf5b886e19933c1fa41dd Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kioslave/audiocd/HACKING | 25 + kioslave/audiocd/Makefile.am | 33 + kioslave/audiocd/audiocd.cpp | 1159 ++++++++++++++++++++ kioslave/audiocd/audiocd.h | 126 +++ kioslave/audiocd/audiocd.protocol | 16 + kioslave/audiocd/audiocd.upd | 4 + kioslave/audiocd/configure.in.bot | 0 kioslave/audiocd/configure.in.in | 46 + kioslave/audiocd/kcmaudiocd/Makefile.am | 17 + kioslave/audiocd/kcmaudiocd/audiocd.desktop | 182 +++ kioslave/audiocd/kcmaudiocd/audiocdconfig.ui | 628 +++++++++++ kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp | 267 +++++ kioslave/audiocd/kcmaudiocd/kcmaudiocd.h | 65 ++ kioslave/audiocd/plugins/Makefile.am | 21 + kioslave/audiocd/plugins/audiocdencoder.cpp | 95 ++ kioslave/audiocd/plugins/audiocdencoder.h | 145 +++ kioslave/audiocd/plugins/flac/Makefile.am | 13 + kioslave/audiocd/plugins/flac/encoderflac.cpp | 197 ++++ kioslave/audiocd/plugins/flac/encoderflac.h | 61 ++ kioslave/audiocd/plugins/lame/Makefile.am | 18 + .../audiocd/plugins/lame/audiocd_lame_encoder.kcfg | 138 +++ .../plugins/lame/audiocd_lame_encoder.kcfgc | 4 + .../audiocd/plugins/lame/collectingprocess.cpp | 134 +++ kioslave/audiocd/plugins/lame/collectingprocess.h | 72 ++ kioslave/audiocd/plugins/lame/encoderlame.cpp | 366 +++++++ kioslave/audiocd/plugins/lame/encoderlame.h | 67 ++ kioslave/audiocd/plugins/lame/encoderlameconfig.ui | 930 ++++++++++++++++ kioslave/audiocd/plugins/vorbis/Makefile.am | 18 + .../plugins/vorbis/audiocd_vorbis_encoder.kcfg | 84 ++ .../plugins/vorbis/audiocd_vorbis_encoder.kcfgc | 4 + kioslave/audiocd/plugins/vorbis/encodervorbis.cpp | 336 ++++++ kioslave/audiocd/plugins/vorbis/encodervorbis.h | 68 ++ .../audiocd/plugins/vorbis/encodervorbisconfig.ui | 425 +++++++ kioslave/audiocd/plugins/wav/Makefile.am | 15 + kioslave/audiocd/plugins/wav/encodercda.cpp | 67 ++ kioslave/audiocd/plugins/wav/encodercda.h | 61 ++ kioslave/audiocd/plugins/wav/encoderwav.cpp | 85 ++ kioslave/audiocd/plugins/wav/encoderwav.h | 56 + kioslave/audiocd/upgrade-metadata.sh | 28 + 39 files changed, 6076 insertions(+) create mode 100644 kioslave/audiocd/HACKING create mode 100644 kioslave/audiocd/Makefile.am create mode 100644 kioslave/audiocd/audiocd.cpp create mode 100644 kioslave/audiocd/audiocd.h create mode 100644 kioslave/audiocd/audiocd.protocol create mode 100644 kioslave/audiocd/audiocd.upd create mode 100644 kioslave/audiocd/configure.in.bot create mode 100644 kioslave/audiocd/configure.in.in create mode 100644 kioslave/audiocd/kcmaudiocd/Makefile.am create mode 100644 kioslave/audiocd/kcmaudiocd/audiocd.desktop create mode 100644 kioslave/audiocd/kcmaudiocd/audiocdconfig.ui create mode 100644 kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp create mode 100644 kioslave/audiocd/kcmaudiocd/kcmaudiocd.h create mode 100644 kioslave/audiocd/plugins/Makefile.am create mode 100644 kioslave/audiocd/plugins/audiocdencoder.cpp create mode 100644 kioslave/audiocd/plugins/audiocdencoder.h create mode 100644 kioslave/audiocd/plugins/flac/Makefile.am create mode 100644 kioslave/audiocd/plugins/flac/encoderflac.cpp create mode 100644 kioslave/audiocd/plugins/flac/encoderflac.h create mode 100644 kioslave/audiocd/plugins/lame/Makefile.am create mode 100644 kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg create mode 100644 kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc create mode 100644 kioslave/audiocd/plugins/lame/collectingprocess.cpp create mode 100644 kioslave/audiocd/plugins/lame/collectingprocess.h create mode 100644 kioslave/audiocd/plugins/lame/encoderlame.cpp create mode 100644 kioslave/audiocd/plugins/lame/encoderlame.h create mode 100644 kioslave/audiocd/plugins/lame/encoderlameconfig.ui create mode 100644 kioslave/audiocd/plugins/vorbis/Makefile.am create mode 100644 kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg create mode 100644 kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc create mode 100644 kioslave/audiocd/plugins/vorbis/encodervorbis.cpp create mode 100644 kioslave/audiocd/plugins/vorbis/encodervorbis.h create mode 100644 kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui create mode 100644 kioslave/audiocd/plugins/wav/Makefile.am create mode 100644 kioslave/audiocd/plugins/wav/encodercda.cpp create mode 100644 kioslave/audiocd/plugins/wav/encodercda.h create mode 100644 kioslave/audiocd/plugins/wav/encoderwav.cpp create mode 100644 kioslave/audiocd/plugins/wav/encoderwav.h create mode 100755 kioslave/audiocd/upgrade-metadata.sh (limited to 'kioslave/audiocd') diff --git a/kioslave/audiocd/HACKING b/kioslave/audiocd/HACKING new file mode 100644 index 00000000..dde6efc1 --- /dev/null +++ b/kioslave/audiocd/HACKING @@ -0,0 +1,25 @@ +// CODE LAYOUT + +audiocd.[h,cpp] - The main ioslave code. It contains the logic to rip audio from cd's. It loads all of the plugins that it can find. When it needs to encode, generate directories (and names of directories) it goes through it's list. audiocd uses libkcddb to retrieve its cddb entries and cdparinoia to retrieve the audio from the CD. + +plugins/audiocdencoder.[h,cpp] - The base class for all of the encoder plugins + +plugins/wav/* - The cda and wav "encoders" +plugins/flac/* - the flac encoder +plugins/lame/* - The mp3 encoder +plugins/vorbis/* - The ogg encoder + +kcmaudiocd/ - kcm configure dialog for the audiocd ioslave. It also loads the plugins and gets their configure dialogs and puts them into the tab dialog. + +// USERS + +Audiocd's "interface" is presented first to the users, It should be simple. The number of files/directories should be kept to a minimum. + +// APP SKIMMING + +Audiocd also has the ability to be "skimmed" by other tools. For example an outside application could retrieve the second track with: + +audiocd:/Wav/Track 02.wav?device=/dev/hd2&fileNameTemplate=Track %{number}&cddbChoice=1 + +This way apps can just query audiocd:/ for CDDB, present them to the users, and rip the tracks all without having to impliment it themselves. + diff --git a/kioslave/audiocd/Makefile.am b/kioslave/audiocd/Makefile.am new file mode 100644 index 00000000..736828e1 --- /dev/null +++ b/kioslave/audiocd/Makefile.am @@ -0,0 +1,33 @@ +if include_kcm_audiocd +AUDIO_CD_SUBDIRS = kcmaudiocd +endif +SUBDIRS = plugins $(AUDIO_CD_SUBDIRS) + +INCLUDES = -I$(top_srcdir)/libkcddb \ + -I$(top_builddir)/libkcddb $(all_includes) \ + -I$(top_srcdir)/kscd $(all_includes) \ + -I$(srcdir)/plugins $(all_includes) + +KDE_CXXFLAGS=$(ENABLE_PERMISSIVE_FLAG) + +kde_module_LTLIBRARIES = kio_audiocd.la + +kio_audiocd_la_SOURCES = audiocd.cpp + +kio_audiocd_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) + +kio_audiocd_la_LIBADD = ./plugins/libaudiocdplugins.la $(LIB_KIO) $(CDPARANOIA_LIBS) $(top_builddir)/libkcddb/libkcddb.la $(top_builddir)/kscd/libkcompactdisc.la + +noinst_HEADERS = audiocd.h + +protocoldir = $(kde_servicesdir) +protocol_DATA = audiocd.protocol + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kio_audiocd.pot + +updatedir = $(kde_datadir)/kconf_update +update_DATA = audiocd.upd +update_SCRIPTS = upgrade-metadata.sh + +audiocd.lo: ../../libkcddb/configbase.h diff --git a/kioslave/audiocd/audiocd.cpp b/kioslave/audiocd/audiocd.cpp new file mode 100644 index 00000000..bd9bb14b --- /dev/null +++ b/kioslave/audiocd/audiocd.cpp @@ -0,0 +1,1159 @@ +/* + * Copyright (C) 2000 Rik Hemsley (rikkus) + * Copyright (C) 2000, 2001, 2002 Michael Matz + * Copyright (C) 2001 Carsten Duvenhorst + * Copyright (C) 2001 Adrian Schroeter + * Copyright (C) 2003 Richard Lärkäng + * Copyright (C) 2003 Scott Wheeler + * Copyright (C) 2004, 2005 Benjamin Meyer + * + * 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 + * ERCHANTABILITY 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 + +extern "C" +{ + #include + #include + void paranoiaCallback(long, int); + + #include + KDE_EXPORT int kdemain(int argc, char ** argv); +} + +#include "audiocd.h" +#include "plugins/audiocdencoder.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// CDDB +#include +#include "kcompactdisc.h" + +using namespace KIO; +using namespace KCDDB; + +#define QFL1(x) QString::fromLatin1(x) +#define DEFAULT_CD_DEVICE "/dev/cdrom" +#define CDDB_INFORMATION "CDDB Information" + +using namespace AudioCD; + +static const KCmdLineOptions options[] = +{ + { "+protocol", I18N_NOOP("Protocol name"), 0 }, + { "+pool", I18N_NOOP("Socket name"), 0 }, + { "+app", I18N_NOOP("Socket name"), 0 }, + KCmdLineLastOption +}; + +int kdemain(int argc, char ** argv) +{ + // KApplication uses libkcddb which needs a valid kapp pointer + // GUIenabled must be true as libkcddb sometimes wants to communicate + // with the user + putenv(strdup("SESSION_MANAGER=")); + KApplication::disableAutoDcopRegistration(); + KCmdLineArgs::init(argc, argv, "kio_audiocd", 0, 0, 0, 0); + KCmdLineArgs::addCmdLineOptions(options); + KApplication app(false, true); + + kdDebug(7117) << "Starting " << getpid() << endl; + + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + AudioCDProtocol slave(args->arg(0), args->arg(1), args->arg(2)); + slave.dispatchLoop(); + + kdDebug(7117) << "Done" << endl; + return 0; +} + +enum Which_dir { + Unknown = 0, // Error + Info, // CDDB info + Root, // The root directory, shows all these :) + FullCD, // Show a single file containing all of the data + EncoderDir // A directory created by an encoder +}; + +class AudioCDProtocol::Private { +public: + Private() : cd(KCompactDisc::Asynchronous) { + clearURLargs(); + s_info = i18n("Information"); + s_fullCD = i18n("Full CD"); + } + + void clearURLargs() { + req_allTracks = false; + which_dir = Unknown; + req_track = -1; + cddbUserChoice = -1; + } + + // The type/which of request + bool req_allTracks; + Which_dir which_dir; + int req_track; + QString fname; + AudioCDEncoder *encoder_dir_type; + + // Misc settings + QString device; // URL settable + int paranoiaLevel; // URL settable + bool reportErrors; + + // Directory strings, never change after init + QString s_info; + QString s_fullCD; + + // Current CD + unsigned discid; + unsigned tracks; + bool trackIsAudio[100]; + KCompactDisc cd; // keep it around so that we don't assume the disk changed between every stat() + + // CDDB items + KCDDB::CDDB::Result cddbResult; + CDInfoList cddbList; + int cddbUserChoice; // URL settable + KCDDB::CDInfo cddbBestChoice; + + // Template for .. + QString fileNameTemplate; // URL settable + QString albumTemplate; // URL settable + QString rsearch; + QString rreplace; + + // Current strings for this CD and or cddb selection + QStringList templateTitles; + QString templateAlbumName; +}; + +int paranoia_read_limited_error; + +AudioCDProtocol::AudioCDProtocol(const QCString & protocol, const QCString & pool, const QCString & app) + : SlaveBase(protocol, pool, app) +{ + d = new Private; + + // Add encoders + AudioCDEncoder::findAllPlugins(this, encoders); + encoderTypeCDA = encoderFromExtension(".cda"); + encoderTypeWAV = encoderFromExtension(".wav"); + encoders.setAutoDelete(true); +} + +AudioCDProtocol::~AudioCDProtocol() +{ + delete d; +} + +AudioCDEncoder *AudioCDProtocol::encoderFromExtension(const QString& extension) +{ + AudioCDEncoder *encoder; + for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){ + if(QString(".")+encoder->fileType() == extension) + return encoder; + } + Q_ASSERT(false); + return NULL; +} + +AudioCDEncoder *AudioCDProtocol::determineEncoder(const QString & filename) +{ + int len = filename.length(); + int pos = filename.findRev('.'); + return encoderFromExtension(filename.right(len - pos)); +} + +struct cdrom_drive * AudioCDProtocol::initRequest(const KURL & url) +{ + if (url.hasHost()) + { + error(KIO::ERR_UNSUPPORTED_ACTION, + i18n("You cannot specify a host with this protocol. " + "Please use the audiocd:/ format instead.")); + return 0; + } + + // Load OUR Settings. + loadSettings(); + // Then url parameters can overrule our settings. + parseURLArgs(url); + + struct cdrom_drive * drive = getDrive(); + if (0 == drive) + return 0; + + // Update our knowledge of the disc + +#if defined(__linux__) + if(drive->ioctl_device_name && drive->ioctl_device_name[0]) + d->cd.setDevice(drive->ioctl_device_name, 50, false); + else + d->cd.setDevice(drive->cdda_device_name, 50, false); +#else + d->cd.setDevice(drive->cdda_device_name, 50, false); +#endif + +#if 0 + // FreeBSD's cdparanoia as of january 5th 2006 has rather broken + // support for non-SCSI devices. Although it finds ATA cdroms just + // fine, there is no straightforward way to discover the device + // name associated with the device, which throws the rest of audiocd + // for a loop. + // + if ( !(drive->dev) || (COOKED_IOCTL == drive->interface) ) + { + // For ATAPI devices, we have no real choice. Use the + // user selected value, even if there is none. + // + kdWarning(7117) << "Found an ATAPI device, assuming it is the one specified by the user." << endl; + d->cd.setDevice( d->device ); + } + else + { + kdDebug(7117) << "Found a SCSI or ATAPICAM device." << endl; + if ( strlen(drive->dev->device_path) > 0 ) + { + d->cd.setDevice( drive->dev->device_path ); + } + else + { + // But the device_path can be empty under some + // circumstances, so build a representation from + // the unit number and SCSI device name. + // + QString devname = QString::fromLatin1( "/dev/%1%2" ) + .arg( drive->dev->given_dev_name ) + .arg( drive->dev->given_unit_number ) ; + kdDebug(7117) << " Using derived name " << devname << endl; + d->cd.setDevice( devname ); + } + } +#endif + + if (d->cd.discId() != d->discid && d->cd.discId() != d->cd.missingDisc){ + d->discid = d->cd.discId(); + d->tracks = d->cd.tracks(); + for(uint i=0; i< d->cd.tracks(); i++) + d->trackIsAudio[i] = d->cd.isAudio(i+1); + + KCDDB::Client c; + d->cddbResult = c.lookup(d->cd.discSignature()); + d->cddbList = c.lookupResponse(); + d->cddbBestChoice = c.bestLookupResponse(); + generateTemplateTitles(); + } + + // Determine what file or folder that is wanted. + d->fname = url.fileName(false); + QString dname = url.directory(true, false); + if (!dname.isEmpty() && dname[0] == '/') + dname = dname.mid(1); + + // Kong issue where they send dirs as files, double check + /* A hack, for when konqi wants to list the directory audiocd:/Bla + it really submits this URL, instead of audiocd:/Bla/ to us. We could + send (in listDir) the UDS_NAME as "Bla/" for directories, but then + konqi shows them as "Bla//" in the status line. */ + // See if it is an encoder directory + AudioCDEncoder *encoder; + for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){ + if(encoder->type() == d->fname){ + dname = d->fname; + d->fname = ""; + break; + } + } + // Other Hard coded directories + if (dname.isEmpty() && (d->fname == d->s_info || d->fname == d->s_fullCD )) + { + dname = d->fname; + d->fname = ""; + } + // end hack + + + // See which directory they want + d->which_dir = Unknown; + for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){ + if(encoder->type() == dname){ + d->which_dir = EncoderDir; + d->encoder_dir_type = encoder; + break; + } + } + if ( Unknown == d->which_dir ){ + if (dname.isEmpty()) + d->which_dir = Root; + else if (dname == d->s_info) + d->which_dir = Info; + else if (dname == d->s_fullCD) + d->which_dir = FullCD; + } + + // See if the url is a track + d->req_track = -1; + if (!d->fname.isEmpty()){ + QString name(d->fname); + + // Remove extension + int dot = name.findRev('.'); + if (dot >= 0) + name.truncate(dot); + + // See if it matches a cddb title + uint trackNumber; + for (trackNumber = 0; trackNumber < d->tracks; trackNumber++){ + if (d->templateTitles[trackNumber] == name) + break; + } + if (trackNumber < d->tracks) + d->req_track = trackNumber; + else { + /* Not found in title list. Try hard to find a number in the + string. */ + unsigned int start = 0; + unsigned int end = 0; + // Find where the numbers start + while (start < name.length()){ + if (name[start++].isDigit()) + break; + } + // Find where the numbers end + for (end = start; end < name.length(); end++) + if (!name[end].isDigit()) + break; + if (start < name.length()){ + bool ok; + // The external representation counts from 1 so subtrac 1. + d->req_track = name.mid(start-1, end - start+2).toInt(&ok) - 1; + if (!ok) + d->req_track = -1; + } + } + } + if (d->req_track >= (int)d->tracks) + d->req_track = -1; + + // Are we in the directory that lists "full CD" files? + d->req_allTracks = (dname.contains(d->s_fullCD)); + + kdDebug(7117) << "dir=" << dname << " file=" << d->fname + << " req_track=" << d->req_track << " which_dir=" << d->which_dir << " full CD?=" << d->req_allTracks << endl; + return drive; +} + +bool AudioCDProtocol::getSectorsForRequest(struct cdrom_drive * drive, long & firstSector, long & lastSector) const +{ + if (d->req_allTracks) + { // we rip all the tracks of the CD + firstSector = cdda_track_firstsector(drive, 1); + lastSector = cdda_track_lastsector(drive, cdda_tracks(drive)); + } + else + { // we only rip the selected track + int trackNumber = d->req_track + 1; + + if (trackNumber <= 0 || trackNumber > cdda_tracks(drive)) + return false; + firstSector = cdda_track_firstsector(drive, trackNumber); + lastSector = cdda_track_lastsector(drive, trackNumber); + } + return true; +} + +void AudioCDProtocol::get(const KURL & url) +{ + struct cdrom_drive * drive = initRequest(url); + if (!drive) + return; + + if( d->fname.contains(i18n(CDDB_INFORMATION))){ + uint choice = 1; + if(d->fname != QString("%1.txt").arg(i18n(CDDB_INFORMATION))){ + choice= d->fname.section('_',1,1).section('.',0,0).toInt(); + } + uint count = 1; + CDInfoList::iterator it; + bool found = false; + for ( it = d->cddbList.begin(); it != d->cddbList.end(); ++it ){ + if(count == choice){ + mimeType("text/html"); + data(QCString( (*it).toString().latin1() )); + // send an empty QByteArray to signal end of data. + data(QByteArray()); + finished(); + found = true; + break; + } + count++; + } + if(!found && d->fname.contains(i18n(CDDB_INFORMATION)+":")){ + mimeType("text/html"); + //data(QCString( d->fname.latin1() )); + // send an empty QByteArray to signal end of data. + data(QByteArray()); + finished(); + found = true; + } + if( !found ) + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + cdda_close(drive); + return; + } + + long firstSector, lastSector; + if (!getSectorsForRequest(drive, firstSector, lastSector)) + { + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + cdda_close(drive); + return; + } + + AudioCDEncoder *encoder = determineEncoder(d->fname); + if(!encoder){ + cdda_close(drive); + return; + } + + KCDDB::CDInfo info; + if(d->cddbResult == KCDDB::CDDB::Success){ + info = d->cddbBestChoice; + + int track = d->req_track; + + // hack + // do we rip the whole CD? + if (d->req_allTracks){ + track = 0; + // YES => the title of the file is the title of the CD + info.trackInfoList[track].title = info.title.utf8().data(); + } + encoder->fillSongInfo(info, track, ""); + } + long totalByteCount = CD_FRAMESIZE_RAW * (lastSector - firstSector + 1); + long time_secs = (8 * totalByteCount) / (44100 * 2 * 16); + + unsigned long size = encoder->size(time_secs); + totalSize(size); + emit mimeType(QFL1(encoder->mimeType())); + + // Read data (track/disk) from the cd + paranoiaRead(drive, firstSector, lastSector, encoder, url.fileName(), size); + + // send an empty QByteArray to signal end of data. + data(QByteArray()); + + cdda_close(drive); + + finished(); +} + +void AudioCDProtocol::stat(const KURL & url) +{ + struct cdrom_drive * drive = initRequest(url); + if (!drive) + return; + + bool isFile = !d->fname.isEmpty(); + + // the track number. 0 if ripping + // the whole CD. + uint trackNumber = d->req_track + 1; + + if (!d->req_allTracks) + { // we only want to rip one track. + // does this track exist? + if (isFile && (trackNumber < 1 || trackNumber > d->tracks)) + { + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + return; + } + } + + UDSEntry entry; + + UDSAtom atom; + atom.m_uds = KIO::UDS_NAME; + atom.m_str = url.fileName().replace('/', QFL1("%2F")); + kdDebug(7117) << k_funcinfo << atom.m_str << endl; + entry.append(atom); + + atom.m_uds = KIO::UDS_FILE_TYPE; + atom.m_long = isFile ? S_IFREG : S_IFDIR; + entry.append(atom); + + const mode_t _umask = ::umask(0); + ::umask(_umask); + + atom.m_uds = KIO::UDS_ACCESS; + atom.m_long = 0666 & (~_umask); + entry.append(atom); + + atom.m_uds = KIO::UDS_SIZE; + if (!isFile) + { + atom.m_long = cdda_tracks(drive); + } + else + { + AudioCDEncoder *encoder = determineEncoder(d->fname); + long firstSector, lastSector; + getSectorsForRequest(drive, firstSector, lastSector); + atom.m_long = fileSize(firstSector, lastSector, encoder); + } + + entry.append(atom); + + statEntry(entry); + + cdda_close(drive); + + finished(); +} + +static void app_entry(UDSEntry& e, unsigned int uds, const QString& str) +{ + UDSAtom a; + a.m_uds = uds; + a.m_str = str; + e.append(a); +} + +static void app_entry(UDSEntry& e, unsigned int uds, long l) +{ + UDSAtom a; + a.m_uds = uds; + a.m_long = l; + e.append(a); +} + +static void app_dir(UDSEntry& e, const QString & n, size_t s) +{ + e.clear(); + app_entry(e, KIO::UDS_NAME, QFile::decodeName(n.local8Bit())); + app_entry(e, KIO::UDS_FILE_TYPE, S_IFDIR); + app_entry(e, KIO::UDS_ACCESS, 0400); + app_entry(e, KIO::UDS_SIZE, s); + app_entry(e, KIO::UDS_MIME_TYPE, "inode/directory"); +} + +static void app_file(UDSEntry& e, const QString & n, size_t s) +{ + e.clear(); + app_entry(e, KIO::UDS_NAME, QFile::decodeName(n.local8Bit())); + app_entry(e, KIO::UDS_FILE_TYPE, S_IFREG); + app_entry(e, KIO::UDS_ACCESS, 0400); + app_entry(e, KIO::UDS_SIZE, s); +} + +void AudioCDProtocol::listDir(const KURL & url) +{ + struct cdrom_drive * drive = initRequest(url); + + // Some error checking before proceeding + if (!drive) + return; + + if (d->which_dir == Unknown){ + error(KIO::ERR_DOES_NOT_EXIST, url.path()); + cdda_close(drive); + return; + } + + if ( !d->fname.isEmpty() ){ + error(KIO::ERR_IS_FILE, url.path()); + cdda_close(drive); + return; + } + + // Generate templated names every time + // because the template might have changed. + generateTemplateTitles(); + + UDSEntry entry; + // If the tracks should be listed in this directory + bool list_tracks = true; + + if (d->which_dir == Info){ + CDInfoList::iterator it; + uint count = 1; + for ( it = d->cddbList.begin(); it != d->cddbList.end(); ++it ){ + (*it).toString(); + if(count == 1) + app_file(entry, QString("%1.txt").arg(i18n(CDDB_INFORMATION)), ((*it).toString().length())+1); + else + app_file(entry, QString("%1_%2.txt").arg(i18n(CDDB_INFORMATION)).arg(count), ((*it).toString().length())+1); + count++; + listEntry(entry, false); + } + // Error + if( count == 1 ) { + app_file(entry, QString("%1: %2.txt").arg(i18n(CDDB_INFORMATION)).arg(CDDB::resultToString(d->cddbResult)), ((*it).toString().length())+1); + count++; + listEntry(entry, false); + } + + list_tracks = false; + } + + if (d->which_dir == Root){ + // List virtual directories. + app_dir(entry, d->s_fullCD, encoders.count()); + listEntry(entry, false); + + // Either >0 cddb results or cddb error file + app_dir(entry, d->s_info, d->cddbList.count()); + listEntry(entry, false); + + // List the encoders + AudioCDEncoder *encoder; + for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){ + // Skip the directory that is in the root (you can still go in it, just don't show it) + if( encoder == encoderTypeWAV ) + continue; + app_dir(entry, encoder->type(), d->tracks); + listEntry(entry, false); + } + } + + // Now fill in the tracks for the current directory + if (list_tracks && d->which_dir == FullCD) { + // if we're listing the "full CD" subdirectory : + if ( (d->which_dir == FullCD) ) { + AudioCDEncoder *encoder; + for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){ + if (d->cddbResult != KCDDB::CDDB::Success) + addEntry(d->s_fullCD, encoder, drive, -1); + else + addEntry(d->templateAlbumName, encoder, drive, -1); + } + } + } + + if (list_tracks && d->which_dir != FullCD) { + // listing another dir than the "FullCD" one. + for (uint trackNumber = 1; trackNumber <= d->tracks; trackNumber++) + { + // Skip data tracks + if (!d->trackIsAudio[trackNumber-1]) + continue; + + switch (d->which_dir) { + case Root:{ + addEntry(d->templateTitles[trackNumber - 1], + encoderTypeWAV, drive, trackNumber); + break; + } + case EncoderDir: + addEntry(d->templateTitles[trackNumber - 1], + d->encoder_dir_type, drive, trackNumber); + break; + case Info: + case Unknown: + default: + error(KIO::ERR_INTERNAL, url.path()); + cdda_close(drive); + return; + } + } + } + + totalSize(entry.count()); + listEntry(entry, true); + cdda_close(drive); + finished(); +} + +void AudioCDProtocol::addEntry(const QString& trackTitle, AudioCDEncoder *encoder, struct cdrom_drive * drive, int trackNo) +{ + if(!encoder || !drive) + return; + + long theFileSize = 0; + if (trackNo == -1) + { // adding entry for the full CD + theFileSize = fileSize(cdda_track_firstsector(drive, 1), + cdda_track_lastsector(drive, cdda_tracks(drive)), + encoder); + } + else + { // adding one regular track + long firstSector = cdda_track_firstsector(drive, trackNo); + long lastSector = cdda_track_lastsector(drive, trackNo); + theFileSize = fileSize(firstSector, lastSector, encoder); + } + UDSEntry entry; + app_file(entry, trackTitle + QString(".")+encoder->fileType(), theFileSize); + listEntry(entry, false); +} + +long AudioCDProtocol::fileSize(long firstSector, long lastSector, AudioCDEncoder *encoder) +{ + if(!encoder) + return 0; + + long filesize = CD_FRAMESIZE_RAW * ( lastSector - firstSector + 1 ); + long length_seconds = (filesize) / 176400; + + return encoder->size(length_seconds); +} + +struct cdrom_drive *AudioCDProtocol::getDrive() +{ + QCString device(QFile::encodeName(d->device)); + + struct cdrom_drive * drive = 0; + + if (!device.isEmpty() && device != "/") + drive = cdda_identify(device, CDDA_MESSAGE_PRINTIT, 0); + else + { + drive = cdda_find_a_cdrom(CDDA_MESSAGE_PRINTIT, 0); + + if (0 == drive) + { + if (QFile(QFile::decodeName(DEFAULT_CD_DEVICE)).exists()) + drive = cdda_identify(DEFAULT_CD_DEVICE, CDDA_MESSAGE_PRINTIT, 0); + } + } + + if (0 == drive) { + kdDebug(7117) << "Can't find an audio CD on: \"" << d->device << "\"" << endl; + + QFileInfo fi(d->device); + if(!fi.isReadable()) + error(KIO::ERR_SLAVE_DEFINED, i18n("Device doesn't have read permissions for this account. Check the read permissions on the device.")); + else if(!fi.isWritable()) + error(KIO::ERR_SLAVE_DEFINED, i18n("Device doesn't have write permissions for this account. Check the write permissions on the device.")); + else if(!fi.exists()) + error(KIO::ERR_DOES_NOT_EXIST, d->device); + else error(KIO::ERR_SLAVE_DEFINED, +i18n("Unknown error. If you have a cd in the drive try running cdparanoia -vsQ as yourself (not root). Do you see a track list? If not, make sure you have permission to access the CD device. If you are using SCSI emulation (possible if you have an IDE CD writer) then make sure you check that you have read and write permissions on the generic SCSI device, which is probably /dev/sg0, /dev/sg1, etc.. If it still does not work, try typing audiocd:/?device=/dev/sg0 (or similar) to tell kio_audiocd which device your CD-ROM is.")); + return 0; + } + + if (0 != cdda_open(drive)) + { + kdDebug(7117) << "cdda_open failed" << endl; + error(KIO::ERR_CANNOT_OPEN_FOR_READING, d->device); + cdda_close(drive); + return 0; + } + + return drive; +} + +void AudioCDProtocol::paranoiaRead( + struct cdrom_drive * drive, + long firstSector, + long lastSector, + AudioCDEncoder* encoder, + const QString& fileName, + unsigned long size + ) +{ + if(!encoder || !drive) + return; + + cdrom_paranoia * paranoia = paranoia_init(drive); + if (0 == paranoia) { + kdDebug(7117) << "paranoia_init failed" << endl; + return; + } + + int paranoiaLevel = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP; + switch (d->paranoiaLevel) + { + case 0: + paranoiaLevel = PARANOIA_MODE_DISABLE; + break; + + case 1: + paranoiaLevel |= PARANOIA_MODE_OVERLAP; + paranoiaLevel &= ~PARANOIA_MODE_VERIFY; + break; + + case 2: + paranoiaLevel |= PARANOIA_MODE_NEVERSKIP; + default: + break; + } + + paranoia_modeset(paranoia, paranoiaLevel); + + cdda_verbose_set(drive, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT); + + paranoia_seek(paranoia, firstSector, SEEK_SET); + + long currentSector(firstSector); + + unsigned long processed = encoder->readInit(CD_FRAMESIZE_RAW * (lastSector - firstSector + 1)); + // TODO test for errors (processed<0)? + processedSize(processed); + bool ok = true; + + unsigned long lastSize = size; + unsigned long diff = 0; + + paranoia_read_limited_error = 0; + int warned = 0; + while (currentSector <= lastSector) + { + // TODO make the 5 configurable? The default in the lib is 20 fyi + int16_t * buf = paranoia_read_limited(paranoia, paranoiaCallback, 5); + if( warned == 0 && paranoia_read_limited_error >= 5 && d->reportErrors ){ + warning(i18n("AudioCD: Disk damage detected on this track, risk of data corruption.")); + warned = 1; + } + if (0 == buf) { + kdDebug(7117) << "Unrecoverable error in paranoia_read" << endl; + ok = false; + error( ERR_SLAVE_DEFINED, i18n( "Error reading audio data for %1 from the CD" ).arg( fileName ) ); + break; + } + + ++currentSector; + + int encoderProcessed = encoder->read(buf, CD_FRAMESAMPLES); + if(encoderProcessed == -1){ + kdDebug(7117) << "Encoder processing error, stopping." << endl; + ok = false; + QString errMsg = i18n( "Couldn't read %1: encoding failed" ).arg( fileName ); + QString details = encoder->lastErrorMessage(); + if ( !details.isEmpty() ) + errMsg += "\n" + details; + error( ERR_SLAVE_DEFINED, errMsg ); + break; + } + processed += encoderProcessed; + + /** + * Because compression size is so 'unknown' use some guesswork + * + * 1) First assume that the reported size is correct and + * only change the totalSize if the guess it outside a range of %5. + * 2) Only increase in size unless the decrease is %5 of last estimate. + * This prevents continues small changes which is just annoying. + */ + unsigned long end = lastSector - firstSector; + unsigned long cur = currentSector - firstSector; + unsigned long estSize = (processed / cur ) * end; + + // If our guess is within 5% of reported + // size then use the reported size. + unsigned long guess = (long)((100/(float)size)*estSize); + if((guess > 97 && guess < 103) || estSize == 0){ + if(processed > lastSize){ + totalSize(processed+1); + lastSize = processed; + } + } + else{ + float percentDone = ((float)cur/(float)end); + // Calculate estimated amount that will be wrong + diff = estSize - lastSize; + diff = (diff*(unsigned long)((100/(float)end)*(end-cur)))/2; + // Need 1% of data calculated as initial buffer, use %2 to be safe + if( percentDone < .02 ){ + //qDebug("val: %f, diff: %ld", ((float)cur/(float)end), diff); + diff = 0; + } + + // We are growing larger, increase total. + if(lastSize < estSize){ + //qDebug("lastGuess: %ld, guess: %ld diff: %ld", lastSize, estSize, diff); + totalSize(estSize+diff); + lastSize = estSize+diff; + } + else{ + int margin = (int)((percentDone)*75); + // Don't bother really trying until almost half way done. + if( percentDone <= .40 ) + margin = 7; + unsigned long low = lastSize - lastSize/margin; + if(estSize < low){ + //qDebug("low: %ld, estSize: %ld, num: %i", low, estSize, margin); + totalSize( estSize ); + lastSize = estSize; + } + } + } + /** + * End estimation. + */ + //qDebug("processed: %ld, totalSize: %ld", processed, estSize); + processedSize(processed); + } + + if(processed > size) + totalSize(processed); + + long encoderProcessed = encoder->readCleanup(); + if ( encoderProcessed >= 0 ) { + processed += encoderProcessed; + if(processed > size) + totalSize(processed); + processedSize(processed); + } + else if ( ok ) // i.e. no error message already emitted + error( ERR_SLAVE_DEFINED, i18n( "Couldn't read %1: encoding failed" ).arg( fileName ) ); + + paranoia_free(paranoia); + paranoia = 0; +} + +/** + * Read the settings from the URL + * @see loadSettings() + */ +void AudioCDProtocol::parseURLArgs(const KURL & url) +{ + d->clearURLargs(); + + QString query(KURL::decode_string(url.query())); + + if (query.isEmpty() || query[0] != '?') + return; + + query = query.mid(1); // Strip leading '?'. + + QStringList tokens(QStringList::split('&', query)); + + for (QStringList::ConstIterator it(tokens.begin()); it != tokens.end(); ++it) + { + QString token(*it); + + int equalsPos(token.find('=')); + if (-1 == equalsPos) + continue; + + QString attribute(token.left(equalsPos)); + QString value(token.mid(equalsPos + 1)); + + if (attribute == QFL1("device")) + d->device = value; + else if (attribute == QFL1("paranoia_level")) + d->paranoiaLevel = value.toInt(); + else if (attribute == QFL1("fileNameTemplate")) + d->fileNameTemplate = value; + else if (attribute == QFL1("albumNameTemplate")) + d->albumTemplate = value; + else if (attribute == QFL1("cddbChoice")) + d->cddbUserChoice = value.toInt(); + else if (attribute == QFL1("niceLevel")){ + int niceLevel = value.toInt(); + if(setpriority(PRIO_PROCESS, getpid(), niceLevel) != 0) + kdDebug(7117) << "Setting nice level to (" << niceLevel << ") failed." << endl; + } + } +} + +/** + * Read the settings set by the kcm modules + * @see parseURLArgs() + */ +void AudioCDProtocol::loadSettings() +{ + KConfig *config = new KConfig(QFL1("kcmaudiocdrc"), true /*readonly*/, false /*no kdeglobals*/); + + config->setGroup(QFL1("CDDA")); + + if (!config->readBoolEntry(QFL1("autosearch"),true)) { + d->device = config->readEntry(QFL1("device"),QFL1(DEFAULT_CD_DEVICE)); + } + + d->paranoiaLevel = 1; // enable paranoia error correction, but allow skipping + + if (config->readBoolEntry("disable_paranoia",false)) { + d->paranoiaLevel = 0; // disable all paranoia error correction + } + + if (config->readBoolEntry("never_skip",true)) { + d->paranoiaLevel = 2; + // never skip on errors of the medium, should be default for high quality + } + + d->reportErrors = config->readBoolEntry( "report_errors", false ); + + if(config->hasKey("niceLevel")) { + int niceLevel = config->readNumEntry("niceLevel", 0); + if(setpriority(PRIO_PROCESS, getpid(), niceLevel) != 0) + kdDebug(7117) << "Setting nice level to (" << niceLevel << ") failed." << endl; + } + + // The default track filename template + config->setGroup("FileName"); + d->fileNameTemplate = config->readEntry("file_name_template", "%{albumartist} - %{number} - %{title}"); + d->albumTemplate = config->readEntry("album_template", "%{albumartist} - %{albumtitle}"); + d->rsearch = config->readEntry("regexp_search"); + d->rreplace = config->readEntry("regexp_replace"); + // if the regular expressions are enclosed in qoutes. remove them + // otherwise it is not possible to search for a space " ", since an empty (only spaces) value is not + // supported by KConfig, so the space has to be qouted, but then here the regexp searches really for " " + // instead of just the space. Alex + QRegExp qoutedString("^\".*\"$"); + if (qoutedString.exactMatch(d->rsearch)) + { + d->rsearch=d->rsearch.mid(1, d->rsearch.length()-2); + } + if (qoutedString.exactMatch(d->rreplace)) + { + d->rreplace=d->rreplace.mid(1, d->rreplace.length()-2); + } + + // Tell the encoders to load their settings + AudioCDEncoder *encoder = encoders.first(); + while ( encoder ) { + if ( encoder->init() ) { + kdDebug(7117) << "Encoder for " << encoder->type() << " is available." << endl; + encoder->loadSettings(); + encoder = encoders.next(); + } else { + kdDebug(7117) << "Encoder for " << encoder->type() << " is NOT available." << endl; + encoders.remove( encoder ); + encoder = encoders.current(); + } + } + + delete config; +} + +/** + * Generates the track titles from the template using the cddb information. + */ +void AudioCDProtocol::generateTemplateTitles() +{ + d->templateTitles.clear(); + if (d->cddbResult != KCDDB::CDDB::Success) + { + for (unsigned int i = 0; i < d->tracks; i++){ + QString n; + d->templateTitles.append( i18n("Track %1").arg(n.sprintf("%02d", i + 1))); + } + return; + } + + KCDDB::CDInfo info = d->cddbBestChoice; + if(d->cddbUserChoice >= 0 && (((uint)d->cddbUserChoice) < d->cddbList.count())) + info = d->cddbList[d->cddbUserChoice]; + + // Then generate the templates + d->templateTitles.clear(); + for (uint i = 0; i < d->tracks; i++) { + QMap macros; + macros["albumartist"] = info.artist; + macros["albumtitle"] = info.title; + macros["title"] = (info.trackInfoList[i].title); + QString n; + macros["number"] = n.sprintf("%02d", i + 1); + //macros["number"] = QString("%1").arg(i+1, 2, 10); + macros["genre"] = info.genre; + macros["year"] = QString::number(info.year); + + QString title = KMacroExpander::expandMacros(d->fileNameTemplate, macros, '%').replace('/', QFL1("%2F")); + title.replace( QRegExp(d->rsearch), d->rreplace ); + d->templateTitles.append(title); + } + + QMap macros; + macros["albumartist"] = info.artist; + macros["albumtitle"] = info.title; + macros["genre"] = info.genre; + macros["year"] = QString::number(info.year); + d->templateAlbumName = KMacroExpander::expandMacros(d->albumTemplate, macros, '%').replace('/', QFL1("%2F")); + d->templateAlbumName.replace( QRegExp(d->rsearch), d->rreplace ); +} + +/** + * Based upon the cdparinoia ripping application + * Only output BAD stuff + * The higher the paranoia_read_limited_error the worse the problem is + * FYI: PARANOIA_CB_READ & PARANOIA_CB_VERIFY happen continusly when ripping + */ +void paranoiaCallback(long, int function) +{ + switch(function){ + case PARANOIA_CB_VERIFY: + //kdDebug(7117) << "PARANOIA_CB_VERIFY" << endl; + break; + + case PARANOIA_CB_READ: + //kdDebug(7117) << "PARANOIA_CB_READ" << endl; + break; + + case PARANOIA_CB_FIXUP_EDGE: + //kdDebug(7117) << "PARANOIA_CB_FIXUP_EDGE" << endl; + paranoia_read_limited_error = 2; + break; + + case PARANOIA_CB_FIXUP_ATOM: + //kdDebug(7117) << "PARANOIA_CB_FIXUP_ATOM" << endl; + paranoia_read_limited_error = 6; + break; + + case PARANOIA_CB_READERR: + kdDebug(7117) << "PARANOIA_CB_READERR" << endl; + paranoia_read_limited_error = 6; + break; + + case PARANOIA_CB_SKIP: + kdDebug(7117) << "PARANOIA_CB_SKIP" << endl; + paranoia_read_limited_error = 8; + break; + + case PARANOIA_CB_OVERLAP: + //kdDebug(7117) << "PARANOIA_CB_OVERLAP" << endl; + break; + + case PARANOIA_CB_SCRATCH: + kdDebug(7117) << "PARANOIA_CB_SCRATCH" << endl; + paranoia_read_limited_error = 7; + break; + + case PARANOIA_CB_DRIFT: + //kdDebug(7117) << "PARANOIA_CB_DRIFT" << endl; + paranoia_read_limited_error = 4; + break; + + case PARANOIA_CB_FIXUP_DROPPED: + kdDebug(7117) << "PARANOIA_CB_FIXUP_DROPPED" << endl; + paranoia_read_limited_error = 5; + break; + + case PARANOIA_CB_FIXUP_DUPED: + kdDebug(7117) << "PARANOIA_CB_FIXUP_DUPED" << endl; + paranoia_read_limited_error = 5; + break; + } +} + diff --git a/kioslave/audiocd/audiocd.h b/kioslave/audiocd/audiocd.h new file mode 100644 index 00000000..a9038496 --- /dev/null +++ b/kioslave/audiocd/audiocd.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2000 Rik Hemsley (rikkus) + * Copyright (C) 2000, 2001, 2002 Michael Matz + * Copyright (C) 2001 Carsten Duvenhorst + * Copyright (C) 2001 Adrian Schroeter + * Copyright (C) 2003 Richard Lärkäng + * Copyright (C) 2003 Scott Wheeler + * Copyright (C) 2004, 2005 Benjamin Meyer + * + * 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 + * ERCHANTABILITY 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 AUDIO_CD_H +#define AUDIO_CD_H + +#include + +class AudioCDEncoder; + +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: + + AudioCDProtocol(const QCString & protocol, const QCString & pool, const QCString & app); + virtual ~AudioCDProtocol(); + + virtual void get(const KURL &); + virtual void stat(const KURL &); + virtual void listDir(const KURL &); + + protected: + AudioCDEncoder *encoderFromExtension(const QString& extension); + AudioCDEncoder *determineEncoder(const QString & filename); + + struct cdrom_drive *findDrive(bool &noPermission); + void parseURLArgs(const KURL &); + + void loadSettings(); + + /** + * From the request information (Private member "d"), + * get the first and last sector for the request. + * return false if the parameters are invalid (for instance, + * track number which does not exist on this CD) + */ + bool getSectorsForRequest(struct cdrom_drive * drive, + long & firstSector, long & lastSector) const; + + /** + * Give the size in bytes of the space between those two + * sectors, depending on the file type. + */ + long fileSize(long firstSector, long lastSector, AudioCDEncoder *encoder); + + /** + * The heart of this ioslave. + * Reads data off the cd and then passes it to an encoder to encode + */ + void paranoiaRead( + struct cdrom_drive * drive, + long firstSector, + long lastSector, + AudioCDEncoder* encoder, + const QString& fileName, + unsigned long size + ); + + struct cdrom_drive *initRequest(const KURL &); + uint discid(struct cdrom_drive *); + + /** + * Add an entry in the KIO directory, using the title you give, + * it will set the extension itself depending on the fileType. + * You must also give the trackNumber for the size of the file + * to be calculated. + * NOTE: if you want to add a file which is the whole CD, give + * trackNo = -1 + * NOTE2: you can safely add MP3 or OGG files always. the function + * will check if kio_audiocd was compiled with support for those, + * so NO NEED to wrap your calls with #ifdef for lame or vorbis. + * this function will do the right thing. + */ + void addEntry(const QString& trackTitle, AudioCDEncoder *encoder, + struct cdrom_drive * drive, int trackNo); + + class Private; + Private * d; + +private: + + void generateTemplateTitles(); + + QPtrList encoders; + cdrom_drive * getDrive(); + + // These are the only garenteed encoders to be built, the rest + // are dynamic depending on other libraries on the system + AudioCDEncoder *encoderTypeCDA; + AudioCDEncoder *encoderTypeWAV; +}; + +} // end namespace AudioCD + +#endif // AUDIO_CD_H + diff --git a/kioslave/audiocd/audiocd.protocol b/kioslave/audiocd/audiocd.protocol new file mode 100644 index 00000000..7a3f497f --- /dev/null +++ b/kioslave/audiocd/audiocd.protocol @@ -0,0 +1,16 @@ +[Protocol] +exec=kio_audiocd +protocol=audiocd +input=none +output=filesystem +listing=Name,Type,Size,Access +reading=true +writing=false +makedir=false +deleting=false +linking=false +moving=false +Icon=cdaudio_unmount +DocPath=kioslave/audiocd.html +Class=:local +ShowPreviews=false diff --git a/kioslave/audiocd/audiocd.upd b/kioslave/audiocd/audiocd.upd new file mode 100644 index 00000000..f4818e5f --- /dev/null +++ b/kioslave/audiocd/audiocd.upd @@ -0,0 +1,4 @@ +# Update for transport configuration +Id=1 +File=kcmaudiocdrc +Script=upgrade-metadata.sh,bash diff --git a/kioslave/audiocd/configure.in.bot b/kioslave/audiocd/configure.in.bot new file mode 100644 index 00000000..e69de29b diff --git a/kioslave/audiocd/configure.in.in b/kioslave/audiocd/configure.in.in new file mode 100644 index 00000000..073d0f96 --- /dev/null +++ b/kioslave/audiocd/configure.in.in @@ -0,0 +1,46 @@ +AC_DEFUN([AC_CHECK_LIBFLAC], +[ + AC_LANG_SAVE + AC_LANG_C + have_libFLAC=no + KDE_CHECK_HEADER(FLAC/metadata.h, + [ + KDE_CHECK_LIB(FLAC,FLAC__seekable_stream_decoder_process_single, + have_libFLAC=yes) + + ]) + if test "x$have_libFLAC" = "xyes"; then + LIBFLAC="-lFLAC" + AC_DEFINE(HAVE_LIBFLAC, 1, + [Define if you have libFLAC (required for loading FLAC files)]) + fi + AC_SUBST(LIBFLAC) + AC_LANG_RESTORE +]) + +AC_DEFUN([AC_CHECK_LIBOGGFLAC], +[ + AC_LANG_SAVE + AC_LANG_C + have_libOggFLAC=no + KDE_CHECK_HEADER(OggFLAC/seekable_stream_decoder.h, + [ + KDE_CHECK_LIB(OggFLAC,OggFLAC__seekable_stream_decoder_process_single, + have_libOggFLAC=yes,,[-lm -lOggFLAC -lFLAC]) + + ]) + if test "x$have_libOggFLAC" = "xyes"; then + LIBOGGFLAC="-lOggFLAC" + AC_DEFINE(HAVE_LIBOGGFLAC, 1, + [Define if you have libOggFLAC (required for loading OggFLAC files)]) + fi + AC_SUBST(LIBOGGFLAC) + AC_LANG_RESTORE +]) + +AC_ARG_WITH(flac,AC_HELP_STRING([--with-flac],[Enable FLAC support @<:@default=check@:>@]),[flac_test="$withval"],[flac_test="yes"]) + +if test "x$flac_test" = "xyes" ; then + AC_CHECK_LIBFLAC + AC_CHECK_LIBOGGFLAC +fi diff --git a/kioslave/audiocd/kcmaudiocd/Makefile.am b/kioslave/audiocd/kcmaudiocd/Makefile.am new file mode 100644 index 00000000..962c3fa0 --- /dev/null +++ b/kioslave/audiocd/kcmaudiocd/Makefile.am @@ -0,0 +1,17 @@ +kde_module_LTLIBRARIES = kcm_audiocd.la + +kcm_audiocd_la_SOURCES = audiocdconfig.ui kcmaudiocd.cpp + +kcm_audiocd_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kcm_audiocd_la_LIBADD = ../plugins/libaudiocdplugins.la $(LIB_KDEUI) + +INCLUDES = -I$(srcdir)/../plugins -I$(top_srcdir)/libkcddb $(all_includes) + +kcm_audiocd_la_METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kcmaudiocd.pot + +xdg_apps_DATA = audiocd.desktop + +audiocdconfig.cpp: audiocdconfig.h diff --git a/kioslave/audiocd/kcmaudiocd/audiocd.desktop b/kioslave/audiocd/kcmaudiocd/audiocd.desktop new file mode 100644 index 00000000..5a9df9a5 --- /dev/null +++ b/kioslave/audiocd/kcmaudiocd/audiocd.desktop @@ -0,0 +1,182 @@ +[Desktop Entry] +Exec=kcmshell audiocd +Icon=cdaudio_unmount +Type=Application + +X-KDE-ModuleType=Library +X-KDE-Library=audiocd + +Name=Audio CDs +Name[af]=Audio Cds +Name[ar]=أقراص مدمجة صوتية +Name[bg]=Аудио диск +Name[bn]=অডিও সিডি +Name[br]=CD klevet +Name[bs]=Audio CDi +Name[ca]=CD d'àudio +Name[cs]=Zvuková CD +Name[cy]=CDau Sain +Name[da]=Lyd-cd'er +Name[de]=Audio-CDs +Name[el]=CD μουσικής +Name[eo]=Son-KDoj +Name[es]=CDs audio +Name[et]=Audioplaadid +Name[eu]=Audio CD-ak +Name[fa]=دیسکهای فشردۀ صوتی +Name[fi]=Ääni-CD:t +Name[fr]=CD audio +Name[ga]=Dlúthdhioscaí Fuaime +Name[he]=תקליטורי שמע +Name[hi]=ऑडीयो सीडी +Name[hr]=Audio CD-i +Name[hu]=Hang-CD-böngésző +Name[is]=Tónlistardiskaflakkari +Name[it]=CD Audio +Name[ja]=オーディオ CD +Name[kk]=Аудио CD +Name[km]=ស៊ីឌី​អ៉ូឌីយ៉ូ +Name[ko]=오디오 CD +Name[lt]=Audio kompaktai +Name[mk]=Аудио ЦД-а +Name[nb]=Lyd-CD-er +Name[nds]=Klang-CDs +Name[ne]=अडियो सीडी +Name[nl]=Audio-cd's +Name[nn]=Lyd-CD-ar +Name[pa]=ਆਡੀਓ CD +Name[pl]=Przeglądarka audio CD +Name[pt]=CDs de Áudio +Name[pt_BR]=CDs de Áudio +Name[ro]=CD-uri audio +Name[ru]=Аудио CD +Name[se]=Jietna-CD:at +Name[sk]=Zvukové CD +Name[sl]=Avdio CD-ji +Name[sr]=Аудио CD-ови +Name[sr@Latn]=Audio CD-ovi +Name[sv]=Ljud-cd +Name[ta]=கேட்பொலி குறுந்தகடுகள் +Name[tg]=Дискҳои Фишурдаи Садо +Name[th]=ซีดีบันทึกเสียง +Name[tr]=Müzik CD +Name[uk]=Аудіо-КД +Name[uz]=Audio kompakt-disklar +Name[uz@cyrillic]=Аудио компакт-дисклар +Name[ven]=CD ino thetsheleswa +Name[xh]=CDs Zokuvakalayo +Name[zh_CN]=音频 CD +Name[zh_HK]=音樂 CD +Name[zh_TW]=音樂光碟 +Name[zu]=Ama-CD Okuzwakalayo +Comment=Audiocd IO Slave Configuration +Comment[af]=Klank cd Io Slaaf Opstelling +Comment[az]=Name=Audiosd IO Kölələri Quraşdırması +Comment[bg]=Настройване на аудио диска +Comment[bn]=অডিও-সিডি আই/ও স্লেভ কনফিগারেশন +Comment[br]=Kefluniadur Sklav IO Audiocd +Comment[bs]=Podešavanje Audiocd IO Slave +Comment[ca]=Configuració de l'E/S esclava dels CD àudio +Comment[cs]=Nastavení IO klienta pro zvuková CD +Comment[cy]=Gosodiad Gwas IO ar gyfer CDau Sain +Comment[da]=Lyd-cd IO-slave-indstilling +Comment[de]=Einrichtung des Ein-/Ausgabemoduls für Audio-CDs +Comment[el]=Ρύθμιση Audiocd IO Slave +Comment[eo]=Agordo por la muzikdiska sklavo +Comment[es]=Configuración del esclavo de E/S de audiocd +Comment[et]=Audio CD IO mooduli seadistamine +Comment[eu]=Audiocd IO mendekoaren konfigurazioa +Comment[fa]=پیکربندی پی‌رو IO دیسک فشردۀ صوتی +Comment[fi]=Audiocd-siirräntätyöskentelijän asetukset +Comment[fr]=Configuration du module pour CD audio +Comment[gl]=Configuración do escravo IO de audiocd +Comment[he]=שינוי הגדרות פרוטוקול תקליטורי השמע +Comment[hi]=ऑडियो-सीडी आई-ओ स्लेव कॉन्फ़िगरेशन +Comment[hr]=Postava Audiocd IO poslužnika +Comment[hu]=Az audiocd KDE-protokoll beállításai +Comment[is]=Stillingar Audiocd I/O þrælsins +Comment[it]=Configurazione dell'IO Slave per i CD Audio +Comment[ja]=オーディオ CD IO スレーブの設定 +Comment[kk]=AudioCD IO Slave баптауы +Comment[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ IO Slave របស់​ស៊ីឌី​អូឌីយ៉ូ +Comment[ko]=오디오 CD IO 슬레이브 설정 +Comment[lt]=Audio kompakto IO vergo derinimas +Comment[ms]=Penyelarasan Hamba IO Audiocd +Comment[mt]=Konfigurazzjoni tal-iskjav IO AudioCD +Comment[nb]=Oppsett av lyd-CD IO-slave +Comment[nds]=Instellen för dat In-/Utgaavmoduul för Klang-CDs +Comment[ne]=Audiocd IO स्लेभ कन्फिगरेसन +Comment[nl]=AudioCD IO slave instellen +Comment[nn]=Oppsett av IU-slave for lyd-CD +Comment[pa]=ਆਡੀਓ ਸੀਡੀ IO ਸਲੇਵ ਸੰਰਚਨਾ +Comment[pl]=Konfiguracja procedury we/wy dla audio CD +Comment[pt]=Configuração do IO Slave de CDs-Áudio +Comment[pt_BR]=Configuração do Áudio CD Escravo +Comment[ro]=Configurează dispozitivul I/O pentru CD audio +Comment[ru]=Настройка протокола AudioCD +Comment[se]=Heivet SO-šláva jietna-CD:aid várás +Comment[sk]=Nastavenie IO klienta pre zvukové CD +Comment[sl]=Nastavitve Audiocd podrejeni V/I +Comment[sr]=Подешавање Audiocd IO Slave-а +Comment[sr@Latn]=Podešavanje Audiocd IO Slave-a +Comment[sv]=Anpassa I/O-slav för ljud-cd +Comment[ta]=ஒலிக் குறுந்தகடு உள்-வெளி அடிமை வடிவமைப்பு +Comment[tg]=Танзими Фармонбари Ворид/Хориҷи Дискҳои Фишурдаи Садо +Comment[th]=ปรับแต่ง Audiocd IO Slave +Comment[tr]=Müzik CD Yapılandırması +Comment[uk]=Налаштування підлеглого В/В "Audiocd" +Comment[ven]=Nzudzanyo Audiocd IO Slave +Comment[xh]=Video-DVD IO Slave Uqwalaselo +Comment[zh_CN]=音频 CD 输入输出从属模块配置 +Comment[zh_HK]=音樂 CD IO Slave 設定 +Comment[zh_TW]=音樂光碟 IO Slave 組態 +Comment[zu]=Inhlanganiselo ye-Audiocd IO Slave +Keywords=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate +Keywords[bg]=аудио, диск, компактдиск, КД, кодек, музика, Audio CD, CD, Ogg, Vorbis, Encoding, CDDA, Bitrate +Keywords[br]=CD klevet,CD,Ogg,Vorbis,Kodadur,CDDA,Feur +Keywords[ca]=Àudio CD,CD,Ogg,Vorbis,Codificació,CDDA,Taxa de bits +Keywords[cs]=Zvukové CD,CD,Ogg,Vorbis,Kódování,CDDA,Bitrate,CDDB +Keywords[cy]=CD Sain,CD,crynoddisg,Ogg,Vorbis,Amgodio,CDDA,Cyfradd Ddidau +Keywords[da]=Lyd-cd,CD,Ogg,Vorbis,Indkodning,CDDA,Bitrate +Keywords[de]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate,MP3 +Keywords[el]=CD μουσικής,CD,Ogg,Vorbis,Κωδικοποίηση,CDDA,Ρυθμός bit +Keywords[es]=CD de audio,CD,Ogg,Vorbis,Codificación,CDDA,Ratio de bits +Keywords[et]=audio,CD,Ogg,Vorbis,kodeering,CDDA,bitikiirus +Keywords[eu]=Audio CD,CD,Ogg,Vorbis,Kdeketa,CDDA,bit-maiztasuna +Keywords[fa]=دیسک فشرده، دیسک فشرده، Ogg، Vorbis، کدبندی، CDDA، میزان ارسال بیت صوتی +Keywords[fi]=Ääni-CD,CD,Ogg,Vorbis,Koodaus,CDDA,Bittinopeus +Keywords[fr]=CD audio,CD,Ogg,Vorbis,Encodage,CDDA,débit +Keywords[ga]=CD Fuaime,CD,Ogg,Vorbis,Ionchódú,CDDA,Ráta Giotán +Keywords[gl]=Audio CD,CD,Ogg,Vorbis,Codificación,CDDA,Razón de Bits +Keywords[hi]=ऑडियो सीडी,सीडी,ऑग,वॉर्बिस,एनकोडिंग,सीडीडीए,बिटरेट +Keywords[hu]=hang-CD,CD,Ogg,Vorbis,kódolás,CDDA,bitráta +Keywords[it]=CD Audio,CD,Ogg,Vorbis,Codifica,CDDA,Bitrate +Keywords[ja]=オーディオ CD,CD,Ogg,Vorbis,エンコーディング,CDDA,ビットレート +Keywords[km]=ស៊ីឌី​អូឌីយ៉ូ,ស៊ីឌី,Ogg,Vorbis,អ៊ិនកូដ,CDDA,អត្រាប៊ីត +Keywords[ko]=오디오 CD,CD,Ogg,인코딩,CDDA +Keywords[lt]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate,įkodavimas +Keywords[mk]=Аудио CD,CD,Ogg,Vorbis,Кодирање,CDDA,Брзина во битови +Keywords[nb]=Audio CD,CD,Ogg,Vorbis,Koding,CDDA,Bitrate +Keywords[nds]=Audio-CD,Klang-CD,CD,Ogg,Vorbis,Koderen,CDDA,Bitrate +Keywords[ne]=अडियो सीडी,सीडी,अग,भर्बिस,सङ्केतन,सीडीडीए,बिटरेट +Keywords[nl]=Audio-cd,cd,Ogg,Vorbis,Encoding,CDDA,Bitrate +Keywords[nn]=lyd-CD,CD,Ogg,Vorbis,koding,CDDA,bitrate +Keywords[pa]=ਆਡੀਓ CD,CD,Ogg,Vorbis,ਇਕੋਡਿੰਗ,CDDA,ਬਿੱਟਰੇਟ +Keywords[pl]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate, Kodowanie +Keywords[pt]=CD de Áudio,CD,Ogg,Vorbis,Codificação,CDDA,Taxa de Bits +Keywords[pt_BR]=CD de Áudio,CD,Ogg,Vorbis,Codificação,CDDA,Bitrate +Keywords[ro]=CD audio,ogg,vorbis,codare,CDDA,rată de bit +Keywords[ru]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate,битрейт +Keywords[sk]=zvukové CD,CD,Ogg,Vorbis,kódovanie,CDDA,bitová frekvencia +Keywords[sl]=Avdio CD,CD,Ogg,Vorbis,kodiranje,CDDA,bitna hitrost +Keywords[sr]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate, Аудио CD, кодирање,битрата +Keywords[sr@Latn]=Audio CD,CD,Ogg,Vorbis,Encoding,CDDA,Bitrate, Audio CD, kodiranje,bitrata +Keywords[sv]=ljud-cd,cd,Ogg,Vorbis,kodning,CDDA,bithastighet +Keywords[ta]=கேட்பொலி குறுந்தகடு,குறுந்தகடு,ஓஜிஜி,வோர்பிஸ்,குறியிடுதல்,சிடிடேஏ,பிட் மதிப்பு +Keywords[tg]=Диски Фишурдаи Садо,Диски Фишурда,Ogg,Vorbis,Рамзигузор,CDDA,Bitrate +Keywords[th]=ซีดีบันทึกเสียง, ซีดี,Ogg,Vorbis,เข้ารหัส,CDDA,บิตเรต +Keywords[tr]=Ses CD,CD,Ogg,Vorbis,Kodlama,CDDA,Bitrate +Keywords[uk]=Аудіо-КД,КД,Ogg,Vorbis,кодування,CDDA,частота вибірки +Keywords[zh_CN]=音频 CD,CD,Ogg,Vorbis,编码,CDDA,Bitrate + +Categories=Qt;KDE;Settings;X-KDE-settings-sound; diff --git a/kioslave/audiocd/kcmaudiocd/audiocdconfig.ui b/kioslave/audiocd/kcmaudiocd/audiocdconfig.ui new file mode 100644 index 00000000..a742a4cc --- /dev/null +++ b/kioslave/audiocd/kcmaudiocd/audiocdconfig.ui @@ -0,0 +1,628 @@ + +AudiocdConfig + + + AudiocdConfig + + + + 0 + 0 + 640 + 563 + + + + + + + + + unnamed + + + 0 + + + 0 + + + + tabWidget + + + + 5 + 5 + 0 + 0 + + + + + + + + + tab + + + &General + + + + unnamed + + + + Spacer5 + + + Vertical + + + Expanding + + + + 20 + 210 + + + + + + encoderPriority + + + Encoder Priority + + + + unnamed + + + + niceLevel + + + -19 + + + 19 + + + 5 + + + Horizontal + + + NoMarks + + + + + textLabel2 + + + Highest + + + + + textLabel3 + + + Lowest + + + AlignVCenter|AlignRight + + + + + textLabel4 + + + Normal + + + AlignCenter + + + + + + + cd_device_string + + + false + + + /dev/cdrom + + + Specify a location for the drive you want to use. Normally, this is a file inside the /dev folder representing your CD or DVD drive. + + + + + cd_specify_device + + + &Specify CD device: + + + Check this to specify a CD device different from the one which is detected automatically + + + + + ec_enable_check + + + Use &error correction when reading the CD + + + true + + + If you uncheck this option, the slave will not try to use error correction which can be useful for reading damaged CDs. However, this feature can be problematic in some cases, so you can switch it off here. + + + + + ec_skip_check + + + &Skip on errors + + + false + + + + + spacer4_2 + + + Horizontal + + + Fixed + + + + 20 + 20 + + + + + + + + tab + + + &Names + + + + unnamed + + + + fileNameGroupBox + + + File Name (without extension) + + + + unnamed + + + + textLabel1_2 + + + The following macros will be expanded: + + + + + layout7 + + + + unnamed + + + + textLabel13 + + + Genre + + + + + textLabel7 + + + Track Number + + + + + textLabel2_3 + + + %{title} + + + + + textLabel11 + + + Year + + + + + textLabel3_3 + + + Track Title + + + + + textLabel9 + + + Album Artist + + + + + textLabel10 + + + %{year} + + + + + textLabel6 + + + %{albumtitle} + + + + + textLabel5 + + + Album Title + + + + + textLabel12 + + + %{genre} + + + + + textLabel8 + + + %{albumartist} + + + + + textLabel4_2 + + + %{number} + + + + + + + fileNameLineEdit + + + + + + + groupBox2 + + + Name Regular Expression Replacement + + + + unnamed + + + + textLabel2_2 + + + Selection: + + + + + textLabel1 + + + Regular expression used on all file names. For example using selection " " and replace with "_" would replace all the spaces with underlines. + + + + WordBreak|AlignVCenter + + + + + inputlabel + + + Input: + + + + + outputLabel + + + Output: + + + + + exampleLabel + + + Example + + + + + exampleOutput + + + Cool artist - example audio file.wav + + + + + example + + + Cool artist - example audio file.wav + + + + + kcfg_replaceInput + + + + + kcfg_replaceOutput + + + + + textLabel3_2 + + + Replace with: + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + + + spacer4 + + + Vertical + + + Expanding + + + + 21 + 16 + + + + + + fileNameGroupBox_2 + + + Album Name + + + + unnamed + + + + fileNameLabel_2 + + + The following macros will be expanded: + + + + + layout9 + + + + unnamed + + + + textLabel20 + + + Year + + + + + textLabel15 + + + %{albumartist} + + + + + textLabel16 + + + %{year} + + + + + textLabel21 + + + Genre + + + + + textLabel19 + + + Album Artist + + + + + textLabel18 + + + Album Title + + + + + textLabel17 + + + %{genre} + + + + + textLabel14 + + + %{albumtitle} + + + + + + + spacer3 + + + Vertical + + + Expanding + + + + 20 + 31 + + + + + + albumNameLineEdit + + + + + + + + + + + + ec_enable_check + toggled(bool) + ec_skip_check + setEnabled(bool) + + + cd_specify_device + toggled(bool) + cd_device_string + setEnabled(bool) + + + + tabWidget + cd_device_string + ec_enable_check + ec_skip_check + niceLevel + fileNameLineEdit + albumNameLineEdit + kcfg_replaceInput + kcfg_replaceOutput + example + + + kcmodule.h + + + toggleLowpass() + + + + klineedit.h + + diff --git a/kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp new file mode 100644 index 00000000..7551b7ad --- /dev/null +++ b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.cpp @@ -0,0 +1,267 @@ +/* + Copyright (C) 2001 Carsten Duvenhorst + + 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "kcmaudiocd.moc" +#include + +KAudiocdModule::KAudiocdModule(QWidget *parent, const char *name) + : AudiocdConfig(parent, name), configChanged(false) +{ + QString foo = i18n("Report errors found on the cd."); + + setButtons(Default|Apply); + + config = new KConfig("kcmaudiocdrc"); + + QPtrList encoders; + AudioCDEncoder::findAllPlugins(0, encoders); + AudioCDEncoder *encoder; + for ( encoder = encoders.first(); encoder; encoder = encoders.next() ){ + if (encoder->init()) { + KConfigSkeleton *config = NULL; + QWidget *widget = encoder->getConfigureWidget(&config); + if(widget && config){ + tabWidget->addTab(widget, i18n("%1 Encoder").arg(encoder->type())); + KConfigDialogManager *configManager = new KConfigDialogManager(widget, config, QString(encoder->type()+" EncoderConfigManager").latin1()); + encoderSettings.append(configManager); + } + } + } + + load(); + + KConfigDialogManager *widget; + for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){ + connect(widget, SIGNAL(widgetModified()), this, SLOT(slotModuleChanged())); + } + + //CDDA Options + connect(cd_specify_device,SIGNAL(clicked()),this,SLOT(slotConfigChanged())); + connect(ec_enable_check,SIGNAL(clicked()),this,SLOT(slotEcEnable())); + connect(ec_skip_check,SIGNAL(clicked()),SLOT(slotConfigChanged())); + connect(cd_device_string,SIGNAL(textChanged(const QString &)),SLOT(slotConfigChanged())); + connect(niceLevel,SIGNAL(valueChanged(int)),SLOT(slotConfigChanged())); + + // File Name + connect(fileNameLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotConfigChanged())); + connect(albumNameLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotConfigChanged())); + connect( kcfg_replaceInput, SIGNAL( textChanged(const QString&) ), this, SLOT( updateExample() ) ); + connect( kcfg_replaceOutput, SIGNAL( textChanged(const QString&) ), this, SLOT( updateExample() ) ); + connect( example, SIGNAL( textChanged(const QString&) ), this, SLOT( updateExample() ) ); + connect( kcfg_replaceInput, SIGNAL( textChanged(const QString&) ), this, SLOT( slotConfigChanged() ) ); + connect( kcfg_replaceOutput, SIGNAL( textChanged(const QString&) ), this, SLOT( slotConfigChanged() ) ); + connect( example, SIGNAL( textChanged(const QString&) ), this, SLOT( slotConfigChanged() ) ); + + KAboutData *about = + new KAboutData(I18N_NOOP("kcmaudiocd"), I18N_NOOP("KDE Audio CD IO Slave"), + 0, 0, KAboutData::License_GPL, + I18N_NOOP("(c) 2000 - 2005 Audio CD developers")); + + about->addAuthor("Benjamin C. Meyer", I18N_NOOP("Current Maintainer"), "ben@meyerhome.net"); + about->addAuthor("Carsten Duvenhorst", 0, "duvenhorst@duvnet.de"); + setAboutData(about); +} + +KAudiocdModule::~KAudiocdModule() +{ + delete config; +} + +QString removeQoutes(const QString& text) +{ + QString deqoutedString=text; + QRegExp qoutedStringRegExp("^\".*\"$"); + if (qoutedStringRegExp.exactMatch(text)) + { + deqoutedString=text.mid(1, text.length()-2); + } + return deqoutedString; +} + +bool needsQoutes(const QString& text) +{ + QRegExp spaceAtTheBeginning("^\\s+.*$"); + QRegExp spaceAtTheEnd("^.*\\s+$"); + return (spaceAtTheBeginning.exactMatch(text) || spaceAtTheEnd.exactMatch(text)); + + +} + +void KAudiocdModule::updateExample() +{ + QString text = example->text(); + QString deqoutedReplaceInput=removeQoutes(kcfg_replaceInput->text()); + QString deqoutedReplaceOutput=removeQoutes(kcfg_replaceOutput->text()); + + text.replace( QRegExp(deqoutedReplaceInput), deqoutedReplaceOutput ); + exampleOutput->setText(text); +} + +void KAudiocdModule::defaults() { + load( false ); +} + +void KAudiocdModule::save() { + if (!configChanged ) return; + + { + KConfigGroupSaver saver(config, "CDDA"); + + // autosearch is the name of the config option, which has the + // reverse sense of the current text of the configuration option, + // which is specify the device. Therefore, invert the value on write. + // + config->writeEntry("autosearch", !(cd_specify_device->isChecked()) ); + config->writeEntry("device",cd_device_string->text()); + config->writeEntry("disable_paranoia",!(ec_enable_check->isChecked())); + config->writeEntry("never_skip",!(ec_skip_check->isChecked())); + config->writeEntry("niceLevel", niceLevel->value()); + } + + { + KConfigGroupSaver saver(config, "FileName"); + config->writeEntry("file_name_template", fileNameLineEdit->text()); + config->writeEntry("album_name_template", albumNameLineEdit->text()); + config->writeEntry("regexp_example", example->text()); + // save qouted if required + QString replaceInput=kcfg_replaceInput->text(); + QString replaceOutput=kcfg_replaceOutput->text(); + if (needsQoutes(replaceInput)) + { + replaceInput=QString("\"")+replaceInput+QString("\""); + } + if (needsQoutes(replaceOutput)) + { + replaceOutput=QString("\"")+replaceOutput+QString("\""); + } + config->writeEntry("regexp_search", replaceInput); + config->writeEntry("regexp_replace", replaceOutput); + } + + KConfigDialogManager *widget; + for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){ + widget->updateSettings(); + } + + config->sync(); + + configChanged = false; + +} + +void KAudiocdModule::load() { + load( false ); +} + +void KAudiocdModule::load(bool useDefaults) { + + config->setReadDefaults( useDefaults ); + + { + KConfigGroupSaver saver(config, "CDDA"); + + + // Specify <=> not autosearch, as explained above in ::save() + cd_specify_device->setChecked( !(config->readBoolEntry("autosearch",true)) ); + cd_device_string->setText(config->readEntry("device","/dev/cdrom")); + ec_enable_check->setChecked(!(config->readBoolEntry("disable_paranoia",false))); + ec_skip_check->setChecked(!(config->readBoolEntry("never_skip",true))); + niceLevel->setValue(config->readNumEntry("niceLevel", 0)); + } + + { + KConfigGroupSaver saver(config, "FileName"); + fileNameLineEdit->setText(config->readEntry("file_name_template", "%{albumartist} - %{number} - %{title}")); + albumNameLineEdit->setText(config->readEntry("album_name_template", "%{albumartist} - %{albumtitle}")); + kcfg_replaceInput->setText(config->readEntry("regexp_search")); + kcfg_replaceOutput->setText(config->readEntry("regexp_replace")); + example->setText(config->readEntry("example", i18n("Cool artist - example audio file.wav"))); + } + + KConfigDialogManager *widget; + for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){ + widget->updateWidgets(); + } + + emit changed( useDefaults ); +} + +void KAudiocdModule::slotModuleChanged() { + KConfigDialogManager *widget; + for ( widget = encoderSettings.first(); widget; widget = encoderSettings.next() ){ + if(widget->hasChanged()){ + slotConfigChanged(); + break; + } + } +} + +void KAudiocdModule::slotConfigChanged() { + configChanged = true; + emit changed(true); +} + +/* +# slot for the error correction settings +*/ +void KAudiocdModule::slotEcEnable() { + if (!(ec_skip_check->isChecked())) { + ec_skip_check->setChecked(true); + } else { + if (ec_skip_check->isEnabled()) { + ec_skip_check->setChecked(false); + } + } + + slotConfigChanged(); +} + +QString KAudiocdModule::quickHelp() const +{ + return i18n("

Audio CDs

The Audio CD IO-Slave enables you to easily" + " create wav, MP3 or Ogg Vorbis files from your audio CD-ROMs or DVDs." + " The slave is invoked by typing \"audiocd:/\" in Konqueror's location" + " bar. In this module, you can configure" + " encoding, and device settings. Note that MP3 and Ogg" + " Vorbis encoding are only available if KDE was built with a recent" + " version of the LAME or Ogg Vorbis libraries."); +} + +extern "C" +{ + KCModule *create_audiocd(QWidget *parent, const char */*name*/) + { + return new KAudiocdModule(parent, "kcmaudiocd"); + } + +} diff --git a/kioslave/audiocd/kcmaudiocd/kcmaudiocd.h b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.h new file mode 100644 index 00000000..7c322839 --- /dev/null +++ b/kioslave/audiocd/kcmaudiocd/kcmaudiocd.h @@ -0,0 +1,65 @@ +/* + + Copyright (C) 2001 Carsten Duvenhorst + + 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. + + Permission is also granted to link this program with the Qt + library, treating Qt like a library that normally accompanies the + operating system kernel, whether or not that is in fact the case. + +*/ + + +#ifndef KAUDIOCDCONFIG_H +#define KAUDIOCDCONFIG_H + +class KConfigDialogManager; + +#include "audiocdconfig.h" +class KAudiocdModule : public AudiocdConfig +{ + Q_OBJECT + +public: + + KAudiocdModule(QWidget *parent=0, const char *name=0); + ~KAudiocdModule(); + + QString quickHelp() const; + +public slots: + void defaults(); + void save(); + void load(); + void load(bool useDefaults); + +private slots: + void updateExample(); + void slotConfigChanged(); + void slotEcEnable(); + void slotModuleChanged(); + +private: + KConfig *config; + bool configChanged; + + int getBitrateIndex(int value); + + QPtrList encoderSettings; +}; + +#endif // KAUDIOCDCONFIG_H + diff --git a/kioslave/audiocd/plugins/Makefile.am b/kioslave/audiocd/plugins/Makefile.am new file mode 100644 index 00000000..2dd4589c --- /dev/null +++ b/kioslave/audiocd/plugins/Makefile.am @@ -0,0 +1,21 @@ +#if HAVE_VORBIS +#AUDIOCD_PLUGINS_SUBDIR=vorbis +#endif + +SUBDIRS = . wav vorbis lame flac + +INCLUDES = -I$(top_srcdir)/libkcddb $(all_includes) + +lib_LTLIBRARIES = libaudiocdplugins.la + +libaudiocdplugins_la_LIBADD = $(LIB_KDECORE) $(top_builddir)/libkcddb/libkcddb.la + +libaudiocdplugins_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -no-undefined + +libaudiocdplugins_la_SOURCES = audiocdencoder.cpp + +include_HEADERS = audiocdencoder.h + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kio_audiocd.pot + diff --git a/kioslave/audiocd/plugins/audiocdencoder.cpp b/kioslave/audiocd/plugins/audiocdencoder.cpp new file mode 100644 index 00000000..2a18322a --- /dev/null +++ b/kioslave/audiocd/plugins/audiocdencoder.cpp @@ -0,0 +1,95 @@ +/* + Copyright (C) 2004, 2005 Benjamin Meyer + + 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 +#include +#include +#include +#include +#include + +/** + * Attempt to load a plugin and see if it has the symbol create_audiocd_encoders. + * @param libFileName file to try to load. + * @returns pointer to the symbol or NULL + */ +void *loadPlugin(const QString &libFileName) +{ +#ifdef DEBUG + kdDebug(7117) << "Trying to load library. File: \"" << libFileName.latin1() << "\"." << endl; +#endif + KLibLoader *loader = KLibLoader::self(); + if (!loader) + return NULL; +#ifdef DEBUG + kdDebug(7117) << "We have a loader. File: \"" << libFileName.latin1() << "\"." << endl; +#endif + KLibrary *lib = loader->library(libFileName.latin1()); + if (!lib) + return NULL; +#ifdef DEBUG + kdDebug(7117) << "We have a library. File: \"" << libFileName.latin1() << "\"." << endl; +#endif + void *cplugin = lib->symbol("create_audiocd_encoders"); + if (!cplugin) + return NULL; +#ifdef DEBUG + kdDebug(7117) << "We have a plugin. File: \"" << libFileName.latin1() << "\"." << endl; +#endif + return cplugin; +} + +/** + * There might be a "better" way of doing this, but I don't know it, + * but I do know that this does work. :) Feel free to improve the loading system, + * there isn't much code anyway. + */ +void AudioCDEncoder::findAllPlugins(KIO::SlaveBase *slave, QPtrList &encoders){ + QString foundEncoders; + + KStandardDirs standardDirs; + QStringList dirs = standardDirs.findDirs("module", ""); + for (QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it) { + QDir dir(*it); + if (!dir.exists()) { + kdDebug(7117) << "Directory given by KStandardDirs: " << dir.path() << " doesn't exists!" << endl; + continue; + } + dir.setFilter(QDir::Files | QDir::Hidden); + + QStringList list = dir.entryList( "libaudiocd_encoder_*.so"); + kdDebug() << "list " << list << endl; + for (QStringList::ConstIterator it2 = list.begin(); it2 != list.end(); ++it2) + { + QString fileName = *it2; + kdDebug() << fileName << endl; + if (foundEncoders.contains(fileName)) { + kdDebug(7117) << "Warning, encoder has been found twice!" << endl; + continue; + } + foundEncoders.append(fileName); + fileName = fileName.mid(0, fileName.find('.')); + void *function = loadPlugin(fileName); + if(function){ + void (*functionPointer)(KIO::SlaveBase *, QPtrList &) = (void (*)(KIO::SlaveBase *slave, QPtrList &encoders)) function; + functionPointer(slave, encoders); + } + } + } +} + diff --git a/kioslave/audiocd/plugins/audiocdencoder.h b/kioslave/audiocd/plugins/audiocdencoder.h new file mode 100644 index 00000000..089be080 --- /dev/null +++ b/kioslave/audiocd/plugins/audiocdencoder.h @@ -0,0 +1,145 @@ +/* + Copyright (C) 2004, 2005 Benjamin Meyer + + 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 +#include +#include + +class KConfigSkeleton; +using namespace KCDDB; + +class AudioCDEncoder { + +public: + /** + * Constructor. + * @param slave parent that this classes can use to call data() with + * when finished encoding bits. + */ + explicit AudioCDEncoder(KIO::SlaveBase *slave) : ioslave(slave) {}; + + /** + * Deconstructor. + */ + virtual ~AudioCDEncoder(){}; + + /** + * 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. + */ + virtual void loadSettings() = 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) const = 0; + + /** + * @returns the generic user string type/name of this encoder + * Examples: "MP3", "Ogg Vorbis", "Wav", "FID Level 2", etc + */ + virtual QString type() const = 0; + + /** + * @returns the mime type for the files this encoder produces. + * Example: "audio/x-wav" + */ + virtual const char * mimeType() const = 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() const = 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( KCDDB::CDInfo info, int track, const QString &comment ) = 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, -1 on error. + */ + 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; + + /** + * Returns a configure widget for the encoder + */ + virtual QWidget* getConfigureWidget(KConfigSkeleton** manager) const + { Q_UNUSED(manager); return NULL; }; + + /** + * Returns the last error message; called when e.g. read() returns -1. + */ + virtual QString lastErrorMessage() const { return QString::null; } + + /** + * Helper function to load all of the AudioCD Encoders from libraries. + * Uses KStandardDirs to find where libraries could be, opens all of the ones + * that we might own audiocd_encoder_* and then uses the symbol + * create_audiocd_encoders to obtain the encoders from that library. + * @param slave ioslave needed if the plugin is going to be used to encode something. + * @param encoders container for new encoders. + */ + static void findAllPlugins(KIO::SlaveBase *slave, QPtrList &encoders); + +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 + diff --git a/kioslave/audiocd/plugins/flac/Makefile.am b/kioslave/audiocd/plugins/flac/Makefile.am new file mode 100644 index 00000000..4e693c53 --- /dev/null +++ b/kioslave/audiocd/plugins/flac/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = -I$(top_srcdir)/libkcddb -I$(srcdir)/.. $(all_includes) + +kde_module_LTLIBRARIES = libaudiocd_encoder_flac.la + +libaudiocd_encoder_flac_la_SOURCES = encoderflac.cpp + +libaudiocd_encoder_flac_la_LIBADD = $(LIBFLAC) $(LIB_KIO) ../libaudiocdplugins.la + +libaudiocd_encoder_flac_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/audiocd/plugins + +METASOURCES = AUTO diff --git a/kioslave/audiocd/plugins/flac/encoderflac.cpp b/kioslave/audiocd/plugins/flac/encoderflac.cpp new file mode 100644 index 00000000..ece58708 --- /dev/null +++ b/kioslave/audiocd/plugins/flac/encoderflac.cpp @@ -0,0 +1,197 @@ +/* + Copyright (C) 2004 Allan Sandfeld Jensen + Copyright (C) 2005 Benjamin Meyer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "encoderflac.h" + +#ifdef HAVE_LIBFLAC + +#include +#include +#include + +#include +#include + + +extern "C" +{ + KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList &encoders) + { + encoders.append(new EncoderFLAC(slave)); + } +} + +class EncoderFLAC::Private { + +public: + FLAC__StreamEncoder *encoder; + FLAC__StreamMetadata** metadata; + KIO::SlaveBase* ioslave; + unsigned long data; +}; + +static FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + EncoderFLAC::Private *d = (EncoderFLAC::Private*)client_data; + + d->data += bytes; + + QByteArray output; + + if (bytes) { + output.setRawData((const char*)buffer, bytes); + d->ioslave->data(output); + output.resetRawData((const char*)buffer, bytes); + } + + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + +static void MetadataCallback (const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + // We do not have seekable writeback so we just discard the updated metadata + +} + +/* +static FLAC__SeekableStreamEncoderSeekStatus SeekCallback(const FLAC__SeekableStreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{} ; */ + + + + +EncoderFLAC::EncoderFLAC(KIO::SlaveBase *slave) : AudioCDEncoder(slave) { + d = new Private(); + d->ioslave = slave; + d->encoder = 0; +} + +EncoderFLAC::~EncoderFLAC() { + if (d->encoder) FLAC__stream_encoder_delete(d->encoder); + delete d; +} + +bool EncoderFLAC::init(){ + d->encoder = FLAC__stream_encoder_new(); + d->metadata = 0; + d->data = 0; + return true; +} + +void EncoderFLAC::loadSettings() { +// config->setGroup("FLAC"); + +} + +// Estimate size to be 5/8 of uncompresed size. +unsigned long EncoderFLAC::size(long time_secs) const { + long uncompressed = (time_secs * (44100*2*2)); + return (uncompressed/8)*5 + 1000; +} + +long EncoderFLAC::readInit(long size) { + kdDebug(7117) << "EncoderFLAC::readInit() called"<< endl; + d->data = 0; + FLAC__stream_encoder_set_write_callback(d->encoder, WriteCallback); + FLAC__stream_encoder_set_metadata_callback(d->encoder, MetadataCallback); + FLAC__stream_encoder_set_client_data(d->encoder, d); + + // The options match approximely those of flac compression-level-3 + FLAC__stream_encoder_set_do_mid_side_stereo(d->encoder, true); + FLAC__stream_encoder_set_loose_mid_side_stereo(d->encoder, true); // flac -M + FLAC__stream_encoder_set_max_lpc_order(d->encoder, 6); // flac -l6 + FLAC__stream_encoder_set_min_residual_partition_order(d->encoder, 3); + FLAC__stream_encoder_set_max_residual_partition_order(d->encoder, 3); // flac -r3,3 + FLAC__stream_encoder_set_blocksize(d->encoder, 4608); + FLAC__stream_encoder_set_streamable_subset(d->encoder, true); + if (size > 0) + FLAC__stream_encoder_set_total_samples_estimate(d->encoder, size/4); + + FLAC__stream_encoder_init(d->encoder); + return d->data; +} + +long EncoderFLAC::read(int16_t * buf, int frames) +{ + unsigned long olddata = d->data; + FLAC__int32 *buffer = new FLAC__int32[frames*2]; + for(int i=0; iencoder, buffer, frames); + delete[] buffer; + return d->data - olddata; +} + +long EncoderFLAC::readCleanup() +{ + FLAC__stream_encoder_finish(d->encoder); +// FLAC__stream_encoder_delete(d->encoder); + if (d->metadata) { + FLAC__metadata_object_delete(d->metadata[0]); + delete[] d->metadata; + d->metadata = 0; + } + return 0; +} + +void EncoderFLAC::fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ) +{ + d->metadata = new FLAC__StreamMetadata*[1]; + d->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); +// d->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); +// d->metadata[2] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE) + + typedef QPair Comment; + Comment comments[7] = { Comment("Title", info.trackInfoList[track].get("title")), + Comment("Artist", info.get("artist")), + Comment("Album", info.get("title")), + Comment("Genre", info.get("genre")), + Comment("Tracknumber", QString::number(track+1)), + Comment("Comment", comment), + Comment("Date", QString::null )}; + if (info.get("Year").toInt() > 0) { + QDateTime dt(QDate(info.get("Year").toInt(), 1, 1)); + comments[6] = Comment("Date", dt.toString(Qt::ISODate)); + } + + FLAC__StreamMetadata_VorbisComment_Entry entry; + QString field; + QCString cfield; + int num_comments = 0; + + for(int i=0; i<7; i++) { + if (!comments[i].second.toString().isEmpty()) { + field = comments[i].first+"="+comments[i].second.toString(); + cfield = field.utf8(); + entry.entry = (FLAC__byte*)qstrdup(cfield); + entry.length = cfield.length(); + // Insert in vorbiscomment and assign ownership of pointers to FLAC + FLAC__metadata_object_vorbiscomment_insert_comment(d->metadata[0], num_comments, entry, false); + num_comments++; + } + } + + FLAC__stream_encoder_set_metadata(d->encoder, d->metadata, 1); +} + +#endif // HAVE_LIBFLAC + diff --git a/kioslave/audiocd/plugins/flac/encoderflac.h b/kioslave/audiocd/plugins/flac/encoderflac.h new file mode 100644 index 00000000..f933e809 --- /dev/null +++ b/kioslave/audiocd/plugins/flac/encoderflac.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2004 Allan Sandfeld Jensen + Copyright (C) 2005 Benjamin Meyer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef ENCODER_FLAC_H +#define ENCODER_FLAC_H + +#include + +#ifdef HAVE_LIBFLAC + +#include + +/** + * FLAC encoder. + * This encoder is only enabled when HAVE_LIBFLAC is set. + * Check out http://flac.sourceforge.net/ for more information. + */ +class EncoderFLAC : public AudioCDEncoder { + +public: + EncoderFLAC(KIO::SlaveBase *slave); + ~EncoderFLAC(); + + virtual QString type() const { return "FLAC"; }; + virtual bool init(); + virtual void loadSettings(); + virtual unsigned long size(long time_secs) const; + virtual const char * fileType() const { return "flac"; }; + virtual const char * mimeType() const { return "audio/x-flac"; } + virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ); + virtual long readInit(long size); + virtual long read(int16_t * buf, int frames); + virtual long readCleanup(); + + class Private; +private: + Private * d; + +}; + +#endif // HAVE_FLAC + +#endif // ENCODER_FLAC_H + diff --git a/kioslave/audiocd/plugins/lame/Makefile.am b/kioslave/audiocd/plugins/lame/Makefile.am new file mode 100644 index 00000000..49ddddd9 --- /dev/null +++ b/kioslave/audiocd/plugins/lame/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/libkcddb -I$(srcdir)/.. $(taglib_includes) $(all_includes) + +kde_kcfg_DATA = audiocd_lame_encoder.kcfg + +kde_module_LTLIBRARIES = libaudiocd_encoder_lame.la + +libaudiocd_encoder_lame_la_SOURCES = audiocd_lame_encoder.kcfgc encoderlame.cpp encoderlameconfig.ui collectingprocess.cpp + +libaudiocd_encoder_lame_la_LIBADD = $(LIB_KIO) ../libaudiocdplugins.la + +libaudiocd_encoder_lame_la_LDFLAGS = -avoid-version -module -no-undefined $(taglib_libs) $(all_libraries) + +pluginsdir = $(kde_datadir)/audiocd/plugins + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/audiocd_encoder_lame.pot diff --git a/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg new file mode 100644 index 00000000..c752fd81 --- /dev/null +++ b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfg @@ -0,0 +1,138 @@ + + + + + + + + + false + + + + true + + + + + 0 + + + + -2 + + + + + + false + + + + true + + + + false + + + + false + + + + true + + + + + + 10 + 0 + 13 + + + + + + false + + + + false + + + + false + + + + false + + + + true + + + + + 40 + 0 + 13 + + + + 13 + 0 + 13 + + + + 10 + 0 + 13 + + + + + + + false + + + + 18000 + + + + + false + + + + 0 + + + + + false + + + + 900 + + + + + false + + + + 0 + + + + diff --git a/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc new file mode 100644 index 00000000..b96ee704 --- /dev/null +++ b/kioslave/audiocd/plugins/lame/audiocd_lame_encoder.kcfgc @@ -0,0 +1,4 @@ +# Code generation options for kconfig_compiler +File=audiocd_lame_encoder.kcfg +ClassName=Settings +Singleton=true diff --git a/kioslave/audiocd/plugins/lame/collectingprocess.cpp b/kioslave/audiocd/plugins/lame/collectingprocess.cpp new file mode 100644 index 00000000..b3a13755 --- /dev/null +++ b/kioslave/audiocd/plugins/lame/collectingprocess.cpp @@ -0,0 +1,134 @@ +/* + collectingprocess.cpp + + This file is part of libkdepim. + Copyright (c) 2004 Ingo Kloecker + + This library 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 library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "collectingprocess.h" + +#include + +#include + +struct CollectingProcess::Private { + Private() : stdoutSize( 0 ), stderrSize( 0 ) + {} + + uint stdoutSize; + QValueList stdoutBuffer; + uint stderrSize; + QValueList stderrBuffer; +}; + + +CollectingProcess::CollectingProcess( QObject * parent, const char * name ) + : KProcess( parent, name ) +{ + d = new Private(); +} + +CollectingProcess::~CollectingProcess() { + delete d; d = 0; +} + +bool CollectingProcess::start( RunMode runmode, Communication comm ) { + // prevent duplicate connection + disconnect( this, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) ); + if ( comm & Stdout ) { + connect( this, SIGNAL( receivedStdout( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStdout( KProcess *, char *, int ) ) ); + } + // prevent duplicate connection + disconnect( this, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + if ( comm & Stderr ) { + connect( this, SIGNAL( receivedStderr( KProcess *, char *, int ) ), + this, SLOT( slotReceivedStderr( KProcess *, char *, int ) ) ); + } + return KProcess::start( runmode, comm ); +} + +void CollectingProcess::slotReceivedStdout( KProcess *, char *buf, int len ) +{ + QByteArray b; + b.duplicate( buf, len ); + d->stdoutBuffer.append( b ); + d->stdoutSize += len; +} + +void CollectingProcess::slotReceivedStderr( KProcess *, char *buf, int len ) +{ + QByteArray b; + b.duplicate( buf, len ); + d->stderrBuffer.append( b ); + d->stderrSize += len; +} + +QByteArray CollectingProcess::collectedStdout() +{ + if ( d->stdoutSize == 0 ) { + return QByteArray(); + } + + uint offset = 0; + QByteArray b( d->stdoutSize ); + for ( QValueList::const_iterator it = d->stdoutBuffer.begin(); + it != d->stdoutBuffer.end(); + ++it ) { + memcpy( b.data() + offset, (*it).data(), (*it).size() ); + offset += (*it).size(); + } + d->stdoutBuffer.clear(); + d->stdoutSize = 0; + + return b; +} + +QByteArray CollectingProcess::collectedStderr() +{ + if ( d->stderrSize == 0 ) { + return QByteArray(); + } + + uint offset = 0; + QByteArray b( d->stderrSize ); + for ( QValueList::const_iterator it = d->stderrBuffer.begin(); + it != d->stderrBuffer.end(); + ++it ) { + memcpy( b.data() + offset, (*it).data(), (*it).size() ); + offset += (*it).size(); + } + d->stderrBuffer.clear(); + d->stderrSize = 0; + + return b; +} + +#include "collectingprocess.moc" diff --git a/kioslave/audiocd/plugins/lame/collectingprocess.h b/kioslave/audiocd/plugins/lame/collectingprocess.h new file mode 100644 index 00000000..7549fee3 --- /dev/null +++ b/kioslave/audiocd/plugins/lame/collectingprocess.h @@ -0,0 +1,72 @@ +/* -*- mode: C++ -*- + collectingprocess.h + + This file is a copy of the collectingprocess.h which is part of kdepim/libkdepim. + Copyright (c) 2004 Ingo Kloecker + + This library 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 library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef __COLLECTINGPROCESS_H__ +#define __COLLECTINGPROCESS_H__ + +#include + +/** + * @short An output collecting KProcess class. + * + * This class simplifies the usage of KProcess by collecting all output + * (stdout/stderr) of the process. + * + * @author Ingo Kloecker + */ +class CollectingProcess : public KProcess { + Q_OBJECT +public: + CollectingProcess( QObject * parent = 0, const char * name = 0 ); + ~CollectingProcess(); + + /** Starts the process in NotifyOnExit mode and writes in to stdin of + the process. + */ + bool start( RunMode runmode, Communication comm ); + + /** Returns the contents of the stdout buffer and clears it afterwards. */ + QByteArray collectedStdout(); + /** Returns the contents of the stderr buffer and clears it afterwards. */ + QByteArray collectedStderr(); + +private slots: + void slotReceivedStdout( KProcess *, char *, int ); + void slotReceivedStderr( KProcess *, char *, int ); + +private: + class Private; + Private * d; +protected: +}; + +#endif // __COLLECTINGPROCESS_H__ diff --git a/kioslave/audiocd/plugins/lame/encoderlame.cpp b/kioslave/audiocd/plugins/lame/encoderlame.cpp new file mode 100644 index 00000000..a00c60fd --- /dev/null +++ b/kioslave/audiocd/plugins/lame/encoderlame.cpp @@ -0,0 +1,366 @@ +/* + Copyright (C) 2005 Benjamin Meyer + + 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 +#include +#include "encoderlame.h" +#include "encoderlameconfig.h" +#include "audiocd_lame_encoder.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "collectingprocess.h" + +extern "C" +{ + KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList &encoders) { + encoders.append(new EncoderLame(slave)); + } +} + +static int bitrates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; + +class EncoderLame::Private +{ +public: + int bitrate; + bool waitingForWrite; + bool processHasExited; + QString lastErrorMessage; + QStringList genreList; + uint lastSize; + KProcess *currentEncodeProcess; + KTempFile *tempFile; +}; + +EncoderLame::EncoderLame(KIO::SlaveBase *slave) : QObject(), AudioCDEncoder(slave) { + d = new Private(); + d->waitingForWrite = false; + d->processHasExited = false; + d->lastSize = 0; + loadSettings(); +} + +EncoderLame::~EncoderLame(){ + delete d; +} + +QWidget* EncoderLame::getConfigureWidget(KConfigSkeleton** manager) const { + (*manager) = Settings::self(); + KGlobal::locale()->insertCatalogue("audiocd_encoder_lame"); + EncoderLameConfig *config = new EncoderLameConfig(); + config->cbr_settings->hide(); + return config; +} + +bool EncoderLame::init(){ + // Determine if lame is installed on the system or not. + if ( KStandardDirs::findExe( "lame" ).isEmpty() ) + return false; + + // Ask lame for the list of genres it knows; otherwise it barfs when doing + // e.g. lame --tg 'Vocal Jazz' + CollectingProcess proc; + proc << "lame" << "--genre-list"; + proc.start(KProcess::Block, KProcess::Stdout); + + if(proc.exitStatus() != 0) + return false; + + const QByteArray data = proc.collectedStdout(); + QString str; + if ( !data.isEmpty() ) + str = QString::fromLocal8Bit( data, data.size() ); + + d->genreList = QStringList::split( '\n', str ); + // Remove the numbers in front of every genre + for( QStringList::Iterator it = d->genreList.begin(); it != d->genreList.end(); ++it ) { + QString& genre = *it; + uint i = 0; + while ( i < genre.length() && ( genre[i].isSpace() || genre[i].isDigit() ) ) + ++i; + genre = genre.mid( i ); + + } + //kdDebug(7117) << "Available genres:" << d->genreList << endl; + + return true; +} + +void EncoderLame::loadSettings(){ + // Generate the command line arguments for the current settings + args.clear(); + + Settings *settings = Settings::self(); + + int quality = settings->quality(); + if (quality < 0 ) quality = quality *-1; + if (quality > 9) quality = 9; + + int method = settings->bitrate_constant() ? 0 : 1 ; + + if (method == 0) { + // Constant Bitrate Encoding + args.append("-b"); + args.append(QString("%1").arg(bitrates[settings->cbr_bitrate()])); + d->bitrate = bitrates[settings->cbr_bitrate()]; + args.append("-q"); + args.append(QString("%1").arg(quality)); + } + else { + // Variable Bitrate Encoding + if (settings->vbr_average_br()) { + args.append("--abr"); + args.append(QString("%1").arg(bitrates[settings->vbr_mean_brate()])); + d->bitrate = bitrates[settings->vbr_mean_brate()]; + if (settings->vbr_min_br()){ + args.append("-b"); + args.append(QString("%1").arg(bitrates[settings->vbr_min_brate()])); + } + if (settings->vbr_min_hard()) + args.append("-F"); + if (settings->vbr_max_br()){ + args.append("-B"); + args.append(QString("%1").arg(bitrates[settings->vbr_max_brate()])); + } + } else { + d->bitrate = 128; + args.append("-V"); + args.append(QString("%1").arg(quality)); + } + if ( !settings->vbr_xing_tag() ) + args.append("-t"); + } + + args.append("-m"); + switch ( settings->stereo() ) { + case 0: + args.append("s"); + break; + case 1: + args.append("j"); + break; + case 2: + args.append("d"); + break; + case 3: + args.append("m"); + break; + default: + args.append("s"); + break; + } + + if(settings->copyright()) + args.append("-c"); + if(!settings->original()) + args.append("-o"); + if(settings->iso()) + args.append("--strictly-enforce-ISO"); + if(settings->crc()) + args.append("-p"); + + if ( settings->enable_lowpass() ) { + args.append("--lowpass"); + args.append(QString("%1").arg(settings->lowfilterfreq())); + + if (settings->set_lpf_width()){ + args.append("--lowpass-width"); + args.append(QString("%1").arg(settings->lowfilterwidth())); + } + } + + if ( settings->enable_highpass()) { + args.append("--hipass"); + args.append(QString("%1").arg(settings->highfilterfreq())); + + if (settings->set_hpf_width()){ + args.append("--hipass-width"); + args.append(QString("%1").arg(settings->highfilterwidth())); + } + } +} + +unsigned long EncoderLame::size(long time_secs) const { + return (time_secs * d->bitrate * 1000)/8; +} + +long EncoderLame::readInit(long /*size*/){ + // Create KProcess + d->currentEncodeProcess = new KProcess(0); + QString prefix = locateLocal("tmp", ""); + d->tempFile = new KTempFile(prefix, ".mp3"); + d->tempFile->setAutoDelete(true); + d->lastErrorMessage = QString::null; + d->processHasExited = false; + + // -x bitswap + // -r raw/pcm + // -s 44.1 (because it is raw you have to specify this) +#if __BYTE_ORDER == __LITTLE_ENDIAN + *(d->currentEncodeProcess) << "lame" << "--verbose" << "-x" << "-r" << "-s" << "44.1"; +#else + *(d->currentEncodeProcess) << "lame" << "--verbose" << "-r" << "-s" << "44.1"; +#endif + + *(d->currentEncodeProcess) << args; + if(Settings::self()->id3_tag()) + *d->currentEncodeProcess << trackInfo; + + // Read in stdin, output to the temp file + *d->currentEncodeProcess << "-" << d->tempFile->name().latin1(); + + //kdDebug(7117) << d->currentEncodeProcess->args() << endl; + + + connect(d->currentEncodeProcess, SIGNAL(receivedStdout(KProcess *, char *, int)), + this, SLOT(receivedStdout(KProcess *, char *, int))); + connect(d->currentEncodeProcess, SIGNAL(receivedStderr(KProcess *, char *, int)), + this, SLOT(receivedStderr(KProcess *, char *, int))); + connect(d->currentEncodeProcess, SIGNAL(wroteStdin(KProcess *)), + this, SLOT(wroteStdin(KProcess *))); + + connect(d->currentEncodeProcess, SIGNAL(processExited(KProcess *)), + this, SLOT(processExited(KProcess *))); + + // Launch! + d->currentEncodeProcess->start(KProcess::NotifyOnExit, KShellProcess::All); + return 0; +} + +void EncoderLame::processExited ( KProcess *process ){ + kdDebug(7117) << "Lame Encoding process exited with: " << process->exitStatus() << endl; + d->processHasExited = true; +} + +void EncoderLame::receivedStderr( KProcess * /*process*/, char *buffer, int /*buflen*/ ){ + kdDebug(7117) << "Lame stderr: " << buffer << endl; + if ( !d->lastErrorMessage.isEmpty() ) + d->lastErrorMessage += '\t'; + d->lastErrorMessage += QString::fromLocal8Bit( buffer ); +} + +void EncoderLame::receivedStdout( KProcess * /*process*/, char *buffer, int /*length*/ ){ + kdDebug(7117) << "Lame stdout: " << buffer << endl; +} + +void EncoderLame::wroteStdin( KProcess * /*procces*/ ){ + d->waitingForWrite = false; +} + +long EncoderLame::read(int16_t *buf, int frames){ + if(!d->currentEncodeProcess) + return 0; + if (d->processHasExited) + return -1; + + // Pipe the raw data to lame + char * cbuf = reinterpret_cast(buf); + d->currentEncodeProcess->writeStdin( cbuf, frames*4); + + // We can't return until the buffer has been written + d->waitingForWrite = true; + while(d->waitingForWrite && d->currentEncodeProcess->isRunning()){ + kapp->processEvents(); + usleep(1); + } + + // Determine the file size increase + QFileInfo file(d->tempFile->name()); + uint change = file.size() - d->lastSize; + d->lastSize = file.size(); + return change; +} + +long EncoderLame::readCleanup(){ + if(!d->currentEncodeProcess) + return 0; + + // Let lame tag the first frame of the mp3 + d->currentEncodeProcess->closeStdin(); + while( d->currentEncodeProcess->isRunning()){ + kapp->processEvents(); + usleep(1); + } + + // Now copy the file out of the temp into kio + QFile file( d->tempFile->name() ); + if ( file.open( IO_ReadOnly ) ) { + QByteArray output; + char data[1024]; + while ( !file.atEnd() ) { + uint read = file.readBlock(data, 1024); + output.setRawData(data, read); + ioslave->data(output); + output.resetRawData(data, read); + } + file.close(); + } + + // cleanup the process and temp + delete d->currentEncodeProcess; + delete d->tempFile; + d->lastSize = 0; + + return 0; +} + +void EncoderLame::fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ){ + trackInfo.clear(); + trackInfo.append("--tt"); + trackInfo.append(info.trackInfoList[track].get("title").toString()); + + trackInfo.append("--ta"); + trackInfo.append(info.get("artist").toString()); + + trackInfo.append("--tl"); + trackInfo.append(info.get("title").toString()); + + trackInfo.append("--ty"); + trackInfo.append(QString("%1").arg(info.get("year").toString())); + + trackInfo.append("--tc"); + trackInfo.append(comment); + + trackInfo.append("--tn"); + trackInfo.append(QString("%1").arg(track+1)); + + const QString genre = info.get( "genre" ).toString(); + if ( d->genreList.find( genre ) != d->genreList.end() ) + { + trackInfo.append("--tg"); + trackInfo.append(genre); + } +} + + +QString EncoderLame::lastErrorMessage() const +{ + return d->lastErrorMessage; +} + +#include "encoderlame.moc" diff --git a/kioslave/audiocd/plugins/lame/encoderlame.h b/kioslave/audiocd/plugins/lame/encoderlame.h new file mode 100644 index 00000000..76aeb59b --- /dev/null +++ b/kioslave/audiocd/plugins/lame/encoderlame.h @@ -0,0 +1,67 @@ +/* + Copyright (C) 2005 Benjamin Meyer + + 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_LAME_H +#define ENCODER_LAME_H + +#include "audiocdencoder.h" + +class KProcess; + +/** + * MP3 encoder using the LAME encoder. + * Go to http://lame.sourceforge.net/ for lots of information. + */ +class EncoderLame : public QObject, public AudioCDEncoder { + +Q_OBJECT + +public: + EncoderLame(KIO::SlaveBase *slave); + ~EncoderLame(); + + virtual QString type() const { return "MP3"; }; + virtual bool init(); + virtual void loadSettings(); + virtual unsigned long size(long time_secs) const; + virtual const char * fileType() const { return "mp3"; }; + virtual const char * mimeType() const { return "audio/x-mp3"; }; + virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ); + virtual long readInit(long size); + virtual long read(int16_t * buf, int frames); + virtual long readCleanup(); + virtual QString lastErrorMessage() const; + + virtual QWidget* getConfigureWidget(KConfigSkeleton** manager) const; + +protected slots: + void wroteStdin(KProcess *proc); + void receivedStdout(KProcess *, char *buffer, int length); + void receivedStderr(KProcess *proc, char *buffer, int buflen); + void processExited(KProcess *proc); + +private: + class Private; + Private * d; + + QStringList args; + QStringList trackInfo; +}; + +#endif // ENCODER_LAME_H + diff --git a/kioslave/audiocd/plugins/lame/encoderlameconfig.ui b/kioslave/audiocd/plugins/lame/encoderlameconfig.ui new file mode 100644 index 00000000..74060353 --- /dev/null +++ b/kioslave/audiocd/plugins/lame/encoderlameconfig.ui @@ -0,0 +1,930 @@ + +EncoderLameConfig + + + LameConfig + + + + 0 + 0 + 471 + 598 + + + + + unnamed + + + + GroupBox1_2 + + + Options + + + + unnamed + + + 11 + + + 6 + + + + kcfg_copyright + + + Cop&yrighted + + + Mark MP3 file as copyrighted + + + Mark MP3 file as copyrighted. + + + + + kcfg_original + + + Origi&nal + + + true + + + Mark MP3 file as an original + + + Mark MP3 file as an original. + + + + + kcfg_iso + + + &ISO encoding + + + Try to use strict ISO encoding + + + This selects the maximal bitrate used for encoding. + + + + + kcfg_crc + + + &Error protection + + + + + kcfg_id3_tag + + + &Write ID3 tag + + + true + + + If checked and if cddb support is available, an id3 tag will be appended + + + If checked and if cddb support is available, an id3 tag will be appended + + + + + + + buttonGroup1 + + + Encoding Method + + + + unnamed + + + + Layout21 + + + + unnamed + + + 0 + + + 6 + + + + TextLabel3_2 + + + Low + + + + + kcfg_quality + + + -9 + + + 0 + + + 1 + + + -7 + + + Horizontal + + + NoMarks + + + + + TextLabel2_2 + + + High + + + + + + + TextLabel1_2 + + + &Quality: + + + AlignLeft + + + kcfg_quality + + + + + + Stereo + + + + + Joint Stereo + + + + + Dual Channel + + + + + Mono + + + + kcfg_stereo + + + This option controls whether MP3 files are recorded with one or two channels. Note that choosing <i>"Mono"</i> reduces file size, but also kills the stereo signal. + + + + + kcfg_bitrate_constant + + + Constant bitrate + + + + + kcfg_bitrate_variable + + + Variable bitrate + + + true + + + + + + + spacer11 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + vbr_settings + + + true + + + Variable Bitrate Settings + + + + unnamed + + + 11 + + + 6 + + + + kcfg_vbr_average_br + + + Specify avera&ge bitrate: + + + This selects the maximal bitrate used for encoding. + + + + + + 32 kbs + + + + + 40 kbs + + + + + 48 kbs + + + + + 56 kbs + + + + + 64 kbs + + + + + 80 kbs + + + + + 96 kbs + + + + + 112 kbs + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 224 kbs + + + + + 256 kbs + + + + + 320 kbs + + + + kcfg_vbr_max_brate + + + false + + + 13 + + + + + kcfg_vbr_max_br + + + false + + + Maximal bi&trate: + + + This selects the maximal bitrate used for encoding. + + + + + kcfg_vbr_xing_tag + + + Write &Xing VBR tag + + + true + + + This writes additional information related to VBR as introduced by Xing. + + + + + + 32 kbs + + + + + 40 kbs + + + + + 48 kbs + + + + + 56 kbs + + + + + 64 kbs + + + + + 80 kbs + + + + + 96 kbs + + + + + 112 kbs + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 224 kbs + + + + + 256 kbs + + + + + 320 kbs + + + + kcfg_vbr_mean_brate + + + false + + + 9 + + + + + kcfg_vbr_min_hard + + + false + + + Minimal &value is a hard limit + + + + + kcfg_vbr_min_br + + + false + + + Minimal &bitrate: + + + This selects the minimal bitrate used for encoding. + + + + + + 32 kbs + + + + + 40 kbs + + + + + 48 kbs + + + + + 56 kbs + + + + + 64 kbs + + + + + 80 kbs + + + + + 96 kbs + + + + + 112 kbs + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 224 kbs + + + + + 256 kbs + + + + + 320 kbs + + + + kcfg_vbr_min_brate + + + false + + + 1 + + + + + + + cbr_settings + + + Constant Bitrate Settings + + + + unnamed + + + 11 + + + 6 + + + + TextLabel4_2 + + + Bitrate: + + + kcfg_cbr_bitrate + + + + + + 32 kbs + + + + + 40 kbs + + + + + 48 kbs + + + + + 56 kbs + + + + + 64 kbs + + + + + 80 kbs + + + + + 96 kbs + + + + + 112 kbs + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 224 kbs + + + + + 256 kbs + + + + + 320 kbs + + + + kcfg_cbr_bitrate + + + 9 + + + The higher the bitrate, the better the quality and the larger the file. + + + + + + + GroupBox83_2 + + + Filter Settings + + + AlignVCenter|AlignLeft + + + + + + + + unnamed + + + 11 + + + 6 + + + + kcfg_enable_lowpass + + + &Lowpass filter cutoff above + + + + + kcfg_lowfilterfreq + + + false + + + Hz + + + 20000 + + + + + kcfg_enable_highpass + + + &Highpass filter cutoff below + + + + + kcfg_highfilterfreq + + + false + + + Hz + + + 200 + + + + + kcfg_set_lpf_width + + + false + + + Low&pass filter width + + + + + kcfg_lowfilterwidth + + + false + + + Hz + + + 5000 + + + + + kcfg_set_hpf_width + + + false + + + Highpa&ss filter width + + + + + kcfg_highfilterwidth + + + false + + + Hz + + + 50 + + + + + + + + + kcfg_bitrate_constant + toggled(bool) + cbr_settings + setShown(bool) + + + kcfg_enable_highpass + toggled(bool) + kcfg_highfilterfreq + setEnabled(bool) + + + kcfg_enable_highpass + toggled(bool) + kcfg_highfilterwidth + setEnabled(bool) + + + kcfg_enable_lowpass + toggled(bool) + kcfg_lowfilterfreq + setEnabled(bool) + + + kcfg_enable_lowpass + toggled(bool) + kcfg_lowfilterwidth + setEnabled(bool) + + + kcfg_enable_highpass + toggled(bool) + kcfg_set_hpf_width + setEnabled(bool) + + + kcfg_enable_lowpass + toggled(bool) + kcfg_set_lpf_width + setEnabled(bool) + + + kcfg_vbr_average_br + toggled(bool) + kcfg_vbr_max_br + setEnabled(bool) + + + kcfg_vbr_max_br + toggled(bool) + kcfg_vbr_max_brate + setEnabled(bool) + + + kcfg_vbr_average_br + toggled(bool) + kcfg_vbr_mean_brate + setEnabled(bool) + + + kcfg_vbr_average_br + toggled(bool) + kcfg_vbr_min_br + setEnabled(bool) + + + kcfg_vbr_min_br + toggled(bool) + kcfg_vbr_min_brate + setEnabled(bool) + + + kcfg_vbr_min_br + toggled(bool) + kcfg_vbr_min_hard + setEnabled(bool) + + + kcfg_bitrate_variable + toggled(bool) + vbr_settings + setShown(bool) + + + + kcfg_bitrate_variable + kcfg_stereo + kcfg_quality + kcfg_copyright + kcfg_original + kcfg_iso + kcfg_crc + kcfg_id3_tag + kcfg_cbr_bitrate + kcfg_vbr_min_br + kcfg_vbr_min_hard + kcfg_vbr_max_br + kcfg_vbr_average_br + kcfg_vbr_xing_tag + kcfg_vbr_min_brate + kcfg_vbr_max_brate + kcfg_vbr_mean_brate + kcfg_enable_lowpass + kcfg_lowfilterfreq + kcfg_enable_highpass + kcfg_highfilterfreq + kcfg_set_lpf_width + kcfg_lowfilterwidth + kcfg_set_hpf_width + kcfg_highfilterwidth + + + diff --git a/kioslave/audiocd/plugins/vorbis/Makefile.am b/kioslave/audiocd/plugins/vorbis/Makefile.am new file mode 100644 index 00000000..e83240ff --- /dev/null +++ b/kioslave/audiocd/plugins/vorbis/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/libkcddb -I$(srcdir)/.. $(all_includes) + +kde_kcfg_DATA = audiocd_vorbis_encoder.kcfg + +kde_module_LTLIBRARIES = libaudiocd_encoder_vorbis.la + +libaudiocd_encoder_vorbis_la_SOURCES = audiocd_vorbis_encoder.kcfgc encodervorbis.cpp encodervorbisconfig.ui + +libaudiocd_encoder_vorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISFILE_LIBS) $(VORBISENC_LIBS) $(LIB_KIO) ../libaudiocdplugins.la + +libaudiocd_encoder_vorbis_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/audiocd/plugins + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/audiocd_encoder_vorbis.pot diff --git a/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg new file mode 100644 index 00000000..53465f6c --- /dev/null +++ b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfg @@ -0,0 +1,84 @@ + + + + + + + + 0 + + + + + false + + + + false + + + + true + + + + + true + + + + 3 + -1 + 10 + + + + + 1 + 0 + 13 + + + + + 13 + 0 + 13 + + + + + 3 + 0 + 5 + + + + + + + + diff --git a/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc new file mode 100644 index 00000000..e213ccbd --- /dev/null +++ b/kioslave/audiocd/plugins/vorbis/audiocd_vorbis_encoder.kcfgc @@ -0,0 +1,4 @@ +# Code generation options for kconfig_compiler +File=audiocd_vorbis_encoder.kcfg +ClassName=Settings +Singleton=true diff --git a/kioslave/audiocd/plugins/vorbis/encodervorbis.cpp b/kioslave/audiocd/plugins/vorbis/encodervorbis.cpp new file mode 100644 index 00000000..01d8b00b --- /dev/null +++ b/kioslave/audiocd/plugins/vorbis/encodervorbis.cpp @@ -0,0 +1,336 @@ +/* + Copyright (C) 2000 Rik Hemsley (rikkus) + Copyright (C) 2000, 2001, 2002 Michael Matz + Copyright (C) 2001 Carsten Duvenhorst + Copyright (C) 2001 Adrian Schroeter + Copyright (C) 2003 Richard Lärkäng + Copyright (C) 2003 Scott Wheeler + Copyright (C) 2004, 2005 Benjamin Meyer + + 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 "encodervorbis.h" +#include "audiocd_vorbis_encoder.h" +#include "encodervorbisconfig.h" + +#ifdef HAVE_VORBIS + +#include +#include +#include +#include +#include +#include + +#include +#include + +extern "C" +{ + KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList &encoders) + { + encoders.append(new EncoderVorbis(slave)); + } +} + +// these are the approx. bitrates for the current 5 Vorbis modes +static int vorbis_nominal_bitrates[] = { 128, 160, 192, 256, 350 }; +static int vorbis_bitrates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 350 }; + +class EncoderVorbis::Private { + +public: + ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment vc; /* struct that stores all the user comments */ + + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + bool write_vorbis_comments; + long vorbis_bitrate_lower; + long vorbis_bitrate_upper; + long vorbis_bitrate_nominal; + int vorbis_encode_method; + double vorbis_quality; + int vorbis_bitrate; +}; + +EncoderVorbis::EncoderVorbis(KIO::SlaveBase *slave) : AudioCDEncoder(slave) { + d = new Private(); +} + +EncoderVorbis::~EncoderVorbis(){ + delete d; +} + +QWidget* EncoderVorbis::getConfigureWidget(KConfigSkeleton** manager) const { + (*manager) = Settings::self(); + KGlobal::locale()->insertCatalogue("audiocd_encoder_vorbis"); + EncoderVorbisConfig *config = new EncoderVorbisConfig(); + config->kcfg_vorbis_quality->setRange(0.0, 10.0, 0.2, true); + config->vorbis_bitrate_settings->hide(); + return config; +} + +bool EncoderVorbis::init(){ + vorbis_info_init(&d->vi); + vorbis_comment_init(&d->vc); + + vorbis_comment_add_tag + ( + &d->vc, + const_cast("kde-encoder"), + const_cast("kio_audiocd") + ); + return true; +} + +void EncoderVorbis::loadSettings(){ + Settings *settings = Settings::self(); + + d->vorbis_encode_method = settings->vorbis_enc_method(); + d->vorbis_quality = settings->vorbis_quality(); + + if ( settings->set_vorbis_min_br()) { + d->vorbis_bitrate_lower = vorbis_bitrates[settings->vorbis_min_br()] * 1000; + } else { + d->vorbis_bitrate_lower = -1; + } + + if ( settings->set_vorbis_max_br() ) { + d->vorbis_bitrate_upper = vorbis_bitrates[settings->vorbis_max_br()] * 1000; + } else { + d->vorbis_bitrate_upper = -1; + } + + // this is such a hack! + if ( d->vorbis_bitrate_upper != -1 && d->vorbis_bitrate_lower != -1 ) { + d->vorbis_bitrate = 104000; // empirically determined ...?! + } else { + d->vorbis_bitrate = 160 * 1000; + } + + if ( settings->set_vorbis_nominal_br() ) { + d->vorbis_bitrate_nominal = vorbis_nominal_bitrates[settings->vorbis_nominal_br()] * 1000; + d->vorbis_bitrate = d->vorbis_bitrate_nominal; + } else { + d->vorbis_bitrate_nominal = -1; + } + + d->write_vorbis_comments = settings->vorbis_comments(); + + // Now that we have read in the settings apply them to the encoder lib + switch (d->vorbis_encode_method) { + case 0: +/* Support very old libvorbis by simply falling through. */ +#if HAVE_VORBIS >= 2 + vorbis_encode_init_vbr(&d->vi, 2, 44100, d->vorbis_quality/10.0); + break; +#endif + case 1: + vorbis_encode_init(&d->vi, 2, 44100, d->vorbis_bitrate_upper, d->vorbis_bitrate_nominal, d->vorbis_bitrate_lower); + break; + } + +} + +long EncoderVorbis::flush_vorbis(void) { + long processed(0); + + while(vorbis_analysis_blockout(&d->vd,&d->vb)==1) { + /* Support ancient libvorbis (< RC3). */ +#if HAVE_VORBIS >= 2 + vorbis_analysis(&d->vb,NULL); + /* Non-ancient case. */ + vorbis_bitrate_addblock(&d->vb); + + while(vorbis_bitrate_flushpacket(&d->vd, &d->op)) { +#else + vorbis_analysis(&d->vb,&d->op); + /* Make a lexical block to place the #ifdef's nearby. */ + if (1) { +#endif + ogg_stream_packetin(&d->os,&d->op); + while(int result=ogg_stream_pageout(&d->os,&d->og)) { + if (!result) break; + + QByteArray output; + + char * oggheader = reinterpret_cast(d->og.header); + char * oggbody = reinterpret_cast(d->og.body); + + if (d->og.header_len) { + output.setRawData(oggheader, d->og.header_len); + ioslave->data(output); + output.resetRawData(oggheader, d->og.header_len); + } + + if (d->og.body_len) { + output.setRawData(oggbody, d->og.body_len); + ioslave->data(output); + output.resetRawData(oggbody, d->og.body_len); + } + processed += d->og.header_len + d->og.body_len; + } + } + } + return processed; +} + +unsigned long EncoderVorbis::size(long time_secs) const { + long vorbis_size; + switch (d->vorbis_encode_method) + { + case 0: // quality based encoding + +#if HAVE_VORBIS >= 2 // If really old Vorbis is being used, skip this nicely. + + { + // Estimated numbers based on the Vorbis FAQ: + // http://www.xiph.org/archives/vorbis-faq/200203/0030.html + + static long vorbis_q_bitrate[] = { 60, 74, 86, 106, 120, 152, + 183, 207, 239, 309, 440 }; + long quality = static_cast(d->vorbis_quality); + if (quality < 0 || quality > 10) + quality = 3; + vorbis_size = (time_secs * vorbis_q_bitrate[quality] * 1000) / 8; + + break; + } + +#endif // HAVE_VORBIS >= 2 + + default: // bitrate based encoding + vorbis_size = (time_secs * d->vorbis_bitrate/8); + break; + } + + return vorbis_size; +} + +const char * EncoderVorbis::mimeType() const{ + return "audio/vorbis"; +} + +long EncoderVorbis::readInit(long /*size*/){ + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_init(&d->vd,&d->vi); + vorbis_block_init(&d->vd,&d->vb); + + srand(time(NULL)); + ogg_stream_init(&d->os,rand()); + + vorbis_analysis_headerout(&d->vd,&d->vc,&header,&header_comm,&header_code); + + ogg_stream_packetin(&d->os,&header); + ogg_stream_packetin(&d->os,&header_comm); + ogg_stream_packetin(&d->os,&header_code); + + while (int result = ogg_stream_flush(&d->os,&d->og)) { + + if (!result) break; + + QByteArray output; + + char * oggheader = reinterpret_cast(d->og.header); + char * oggbody = reinterpret_cast(d->og.body); + + if (d->og.header_len) { + output.setRawData(oggheader, d->og.header_len); + ioslave->data(output); + output.resetRawData(oggheader, d->og.header_len); + } + + if (d->og.body_len) { + output.setRawData(oggbody, d->og.body_len); + ioslave->data(output); + output.resetRawData(oggbody, d->og.body_len); + } + } + return 0; +} + +long EncoderVorbis::read(int16_t * buf, int frames){ + int i; + float **buffer=vorbis_analysis_buffer(&d->vd,frames); + + /* uninterleave samples */ + for(i=0;ivd,i); + return flush_vorbis(); +} + +long EncoderVorbis::readCleanup(){ + // send end-of-stream and flush the encoder + vorbis_analysis_wrote(&d->vd,0); + long processed = flush_vorbis(); + ogg_stream_clear(&d->os); + vorbis_block_clear(&d->vb); + vorbis_dsp_clear(&d->vd); + vorbis_info_clear(&d->vi); + return processed; +} + +void EncoderVorbis::fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ) +{ + if( !d->write_vorbis_comments ) + return; + + typedef QPair CommentField; + QValueList commentFields; + + commentFields.append(CommentField("title", info.trackInfoList[track].get("title"))); + commentFields.append(CommentField("artist", info.get("artist"))); + commentFields.append(CommentField("album", info.get("title"))); + commentFields.append(CommentField("genre", info.get("genre"))); + commentFields.append(CommentField("tracknumber", QString::number(track+1))); + commentFields.append(CommentField("comment", comment)); + + if (info.get("year").toInt() > 0) { + QDateTime dt(QDate(info.get("year").toInt(), 1, 1)); + commentFields.append(CommentField("date", dt.toString(Qt::ISODate).utf8().data())); + } + + for(QValueListIterator it = commentFields.begin(); it != commentFields.end(); ++it) { + + // if the value is not empty + if(!(*it).second.toString().isEmpty()) { + + char *key = qstrdup((*it).first); + char *value = qstrdup((*it).second.toString().utf8().data()); + + vorbis_comment_add_tag(&d->vc, key, value); + + delete [] key; + delete [] value; + } + } +} + +#endif // HAVE_VORBIS + diff --git a/kioslave/audiocd/plugins/vorbis/encodervorbis.h b/kioslave/audiocd/plugins/vorbis/encodervorbis.h new file mode 100644 index 00000000..28b837ce --- /dev/null +++ b/kioslave/audiocd/plugins/vorbis/encodervorbis.h @@ -0,0 +1,68 @@ +/* + Copyright (C) 2000 Rik Hemsley (rikkus) + Copyright (C) 2000, 2001, 2002 Michael Matz + Copyright (C) 2001 Carsten Duvenhorst + Copyright (C) 2001 Adrian Schroeter + Copyright (C) 2003 Richard Lärkäng + Copyright (C) 2003 Scott Wheeler + Copyright (C) 2004, 2005 Benjamin Meyer + + 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_VORBIS_H +#define ENCODER_VORBIS_H + +#include + +#ifdef HAVE_VORBIS + +#include + +/** + * Ogg Vorbis encoder. + * This encoder is only enabled when HAVE_VORBIS is set. + * Check out http://www.vorbis.com/ for lots of information. + */ +class EncoderVorbis : public AudioCDEncoder { + +public: + EncoderVorbis(KIO::SlaveBase *slave); + ~EncoderVorbis(); + + virtual QString type() const { return "Ogg Vorbis"; }; + virtual bool init(); + virtual void loadSettings(); + virtual unsigned long size(long time_secs) const; + virtual const char * fileType() const { return "ogg"; }; + virtual const char * mimeType() const; + virtual void fillSongInfo( KCDDB::CDInfo info, int track, const QString &comment ); + virtual long readInit(long size); + virtual long read(int16_t * buf, int frames); + virtual long readCleanup(); + virtual QWidget* getConfigureWidget(KConfigSkeleton** manager) const; + +private: + long flush_vorbis(); + + class Private; + Private * d; + +}; + +#endif // HAVE_VORBIS + +#endif // ENCODER_VORBIS_H + diff --git a/kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui b/kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui new file mode 100644 index 00000000..17000745 --- /dev/null +++ b/kioslave/audiocd/plugins/vorbis/encodervorbisconfig.ui @@ -0,0 +1,425 @@ + +EncoderVorbisConfig + + + VorbisConfig + + + + 0 + 0 + 375 + 408 + + + + + unnamed + + + + kcfg_vorbis_enc_method + + + &Encoding Method + + + + unnamed + + + + vorbis_enc_quality + + + Quality based + + + true + + + + + vorbis_enc_bitrate + + + Bitrate based + + + + + + + vorbis_bitrate_settings + + + true + + + Vorbis Bitrate Settings + + + + unnamed + + + 11 + + + 6 + + + + + 32 kbs + + + + + 40 kbs + + + + + 48 kbs + + + + + 56 kbs + + + + + 64 kbs + + + + + 80 kbs + + + + + 96 kbs + + + + + 112 kbs + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 224 kbs + + + + + 256 kbs + + + + + 350 kbs + + + + kcfg_vorbis_min_br + + + false + + + 1 + + + + + + 32 kbs + + + + + 40 kbs + + + + + 48 kbs + + + + + 56 kbs + + + + + 64 kbs + + + + + 80 kbs + + + + + 96 kbs + + + + + 112 kbs + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 224 kbs + + + + + 256 kbs + + + + + 350 kbs + + + + kcfg_vorbis_max_br + + + false + + + 13 + + + + + kcfg_set_vorbis_min_br + + + true + + + M&inimal bitrate: + + + + + kcfg_set_vorbis_max_br + + + true + + + Ma&ximal bitrate: + + + + + + 128 kbs + + + + + 160 kbs + + + + + 192 kbs + + + + + 256 kbs + + + + + 350 kbs + + + + kcfg_vorbis_nominal_br + + + 1 + + + + + kcfg_set_vorbis_nominal_br + + + A&verage bitrate: + + + true + + + + + + + vorbis_quality_settings + + + Vorbis &Quality Setting + + + You can set the quality of the encoded stream here. A higher value implies a higher quality but encodes slower. + + + + unnamed + + + 11 + + + 6 + + + + kcfg_vorbis_quality + + + 0 + + + 0 + + + 10000 + + + 1 + + + Higher is better but slower + + + + + + + GroupBox193 + + + Options + + + + unnamed + + + 11 + + + 6 + + + + kcfg_vorbis_comments + + + true + + + Add &track information + + + Add a description of the song to the file header. This makes it easy for the user to get advanced song information shown by his media player. You can get this information automatically via the Internet. Look at the <i>"CDDB Retrieval"</i> control module for details. + + + + + + + spacer9 + + + Vertical + + + Expanding + + + + 20 + 51 + + + + + + + + vorbis_enc_bitrate + toggled(bool) + vorbis_bitrate_settings + setShown(bool) + + + vorbis_enc_quality + toggled(bool) + vorbis_quality_settings + setShown(bool) + + + + vorbis_enc_quality + kcfg_set_vorbis_min_br + kcfg_set_vorbis_max_br + kcfg_set_vorbis_nominal_br + kcfg_vorbis_min_br + kcfg_vorbis_max_br + kcfg_vorbis_nominal_br + kcfg_vorbis_quality + kcfg_vorbis_comments + + + + knuminput.h + knuminput.h + + diff --git a/kioslave/audiocd/plugins/wav/Makefile.am b/kioslave/audiocd/plugins/wav/Makefile.am new file mode 100644 index 00000000..82e6ab28 --- /dev/null +++ b/kioslave/audiocd/plugins/wav/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(srcdir)/.. $(all_includes) + +INCLUDES = -I$(top_srcdir)/libkcddb + +kde_module_LTLIBRARIES = libaudiocd_encoder_wav.la + +libaudiocd_encoder_wav_la_SOURCES = encoderwav.cpp encodercda.cpp + +libaudiocd_encoder_wav_la_LIBADD = $(LIB_KIO) ../libaudiocdplugins.la $(CDPARANOIA_LIBS) + +libaudiocd_encoder_wav_la_LDFLAGS = -avoid-version -module -no-undefined $(all_libraries) + +pluginsdir = $(kde_datadir)/audiocd/plugins + +METASOURCES = AUTO diff --git a/kioslave/audiocd/plugins/wav/encodercda.cpp b/kioslave/audiocd/plugins/wav/encodercda.cpp new file mode 100644 index 00000000..fb8a794a --- /dev/null +++ b/kioslave/audiocd/plugins/wav/encodercda.cpp @@ -0,0 +1,67 @@ +/* + Copyright (C) 2000 Rik Hemsley (rikkus) + Copyright (C) 2000, 2001, 2002 Michael Matz + Copyright (C) 2001 Carsten Duvenhorst + Copyright (C) 2001 Adrian Schroeter + Copyright (C) 2003 Richard Lärkäng + Copyright (C) 2003 Scott Wheeler + Copyright (C) 2004, 2005 Benjamin Meyer + + 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) const { + //return (time_secs * (44100 * 2 * 16))/8; + return (time_secs) * 176400; +} + +const char * EncoderCda::mimeType() const { + return "audio/x-cda"; +} + +// Remove this by calculating CD_FRAMESIZE_RAW from the frames +extern "C" +{ + #include +} + +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(buf); + output.setRawData(cbuf, CD_FRAMESIZE_RAW); + ioslave->data(output); + output.resetRawData(cbuf, CD_FRAMESIZE_RAW); + return CD_FRAMESIZE_RAW; +} + diff --git a/kioslave/audiocd/plugins/wav/encodercda.h b/kioslave/audiocd/plugins/wav/encodercda.h new file mode 100644 index 00000000..0a95aa51 --- /dev/null +++ b/kioslave/audiocd/plugins/wav/encodercda.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2000 Rik Hemsley (rikkus) + Copyright (C) 2000, 2001, 2002 Michael Matz + Copyright (C) 2001 Carsten Duvenhorst + Copyright (C) 2001 Adrian Schroeter + Copyright (C) 2003 Richard Lärkäng + Copyright (C) 2003 Scott Wheeler + Copyright (C) 2004, 2005 Benjamin Meyer + + 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 +#include +#include +#include + +/** + * Raw cd "encoder" + * Does little more then copy the data and make sure it is in the right + * endian. + */ +class EncoderCda : public AudioCDEncoder { + +public: + EncoderCda(KIO::SlaveBase *slave) : AudioCDEncoder(slave) {}; + ~EncoderCda(){}; + virtual bool init(){ return true; }; + virtual void loadSettings(){}; + virtual unsigned long size(long time_secs) const; + virtual QString type() const { return "CDA"; }; + virtual const char * mimeType() const; + virtual const char * fileType() const { return "cda"; }; + virtual void fillSongInfo( KCDDB::CDInfo, int, const QString &){}; + 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 + diff --git a/kioslave/audiocd/plugins/wav/encoderwav.cpp b/kioslave/audiocd/plugins/wav/encoderwav.cpp new file mode 100644 index 00000000..ad658e2c --- /dev/null +++ b/kioslave/audiocd/plugins/wav/encoderwav.cpp @@ -0,0 +1,85 @@ +/* + Copyright (C) 2000 Rik Hemsley (rikkus) + Copyright (C) 2000, 2001, 2002 Michael Matz + Copyright (C) 2001 Carsten Duvenhorst + Copyright (C) 2001 Adrian Schroeter + Copyright (C) 2003 Richard Lärkäng + Copyright (C) 2003 Scott Wheeler + Copyright (C) 2004, 2005 Benjamin Meyer + + 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 "encoderwav.h" + +extern "C" +{ + KDE_EXPORT void create_audiocd_encoders(KIO::SlaveBase *slave, QPtrList &encoders) + { + encoders.append( new EncoderWav(slave)); + encoders.append( new EncoderCda(slave)); + } +} + +class EncoderWav::Private +{ + public: + +}; + +unsigned long EncoderWav::size(long time_secs) const { + return (EncoderCda::size(time_secs) + 44); +} + +const char * EncoderWav::mimeType() const { + return "audio/x-wav"; +} + +long EncoderWav::readInit(long byteCount){ + static char riffHeader[] = + { + 0x52, 0x49, 0x46, 0x46, // 0 "AIFF" + 0x00, 0x00, 0x00, 0x00, // 4 wavSize + 0x57, 0x41, 0x56, 0x45, // 8 "WAVE" + 0x66, 0x6d, 0x74, 0x20, // 12 "fmt " + 0x10, 0x00, 0x00, 0x00, // 16 + 0x01, 0x00, 0x02, 0x00, // 20 + 0x44, 0xac, 0x00, 0x00, // 24 + 0x10, 0xb1, 0x02, 0x00, // 28 + 0x04, 0x00, 0x10, 0x00, // 32 + 0x64, 0x61, 0x74, 0x61, // 36 "data" + 0x00, 0x00, 0x00, 0x00 // 40 byteCount + }; + + Q_INT32 wavSize(byteCount + 44 - 8); + + + riffHeader[4] = (wavSize >> 0 ) & 0xff; + riffHeader[5] = (wavSize >> 8 ) & 0xff; + riffHeader[6] = (wavSize >> 16) & 0xff; + riffHeader[7] = (wavSize >> 24) & 0xff; + + riffHeader[40] = (byteCount >> 0 ) & 0xff; + riffHeader[41] = (byteCount >> 8 ) & 0xff; + riffHeader[42] = (byteCount >> 16) & 0xff; + riffHeader[43] = (byteCount >> 24) & 0xff; + + QByteArray output; + output.setRawData(riffHeader, 44); + ioslave->data(output); + output.resetRawData(riffHeader, 44); + return 44; +} + diff --git a/kioslave/audiocd/plugins/wav/encoderwav.h b/kioslave/audiocd/plugins/wav/encoderwav.h new file mode 100644 index 00000000..09301ae5 --- /dev/null +++ b/kioslave/audiocd/plugins/wav/encoderwav.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2000 Rik Hemsley (rikkus) + Copyright (C) 2000, 2001, 2002 Michael Matz + Copyright (C) 2001 Carsten Duvenhorst + Copyright (C) 2001 Adrian Schroeter + Copyright (C) 2003 Richard Lärkäng + Copyright (C) 2003 Scott Wheeler + Copyright (C) 2004, 2005 Benjamin Meyer + + 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_WAV_H +#define ENCODER_WAV_H + +#include "encodercda.h" +#include +#include +#include + +/** + * Wav audio "encoder" + * Takes the CDA take and adds the standard wav header. + */ +class EncoderWav : public EncoderCda { + +public: + EncoderWav(KIO::SlaveBase *slave) : EncoderCda(slave) {}; + ~EncoderWav(){}; + virtual bool init(){ return true; }; + virtual unsigned long size(long time_secs) const; + virtual QString type() const { return "Wav"; }; + virtual const char * fileType() const { return "wav"; }; + virtual const char * mimeType() const; + virtual void fillSongInfo( KCDDB::CDInfo, int, const QString &){}; + virtual long readInit(long size); + +private: + class Private; + Private * d; + +}; + +#endif // ENCODER_WAV_H diff --git a/kioslave/audiocd/upgrade-metadata.sh b/kioslave/audiocd/upgrade-metadata.sh new file mode 100755 index 00000000..000323b1 --- /dev/null +++ b/kioslave/audiocd/upgrade-metadata.sh @@ -0,0 +1,28 @@ +#! /usr/bin/env bash + +while read; do + if [ "${REPLY#\[}" != "$REPLY" ] ; then # group name + GROUP="${REPLY:1:${#REPLY}-2}" + continue; + fi + # normal key=value pair: + KEY="${REPLY%%=*}" + VALUE="${REPLY#*=}" + + case "$GROUP/$KEY" in + FileName/file_name_template) + # + # Swap + # + VALUE=`echo $VALUE | sed -e s/%a/%{albumtitle}/g`; + VALUE=`echo $VALUE | sed -e s/%t/%{title}/g`; + VALUE=`echo $VALUE | sed -e s/%n/%{number}/g`; + VALUE=`echo $VALUE | sed -e s/%g/%{genre}/g`; + VALUE=`echo $VALUE | sed -e s/%y/%{year}/g`; + VALUE=`echo $VALUE | sed -e s/%A/%{albumartist}/g`; + echo "[FileName]"; + echo "file_name_template=$VALUE" + echo "# DELETE [FileName]file_name_template" + ;; + esac +done -- cgit v1.2.1