You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
554 lines
13 KiB
554 lines
13 KiB
/*
|
|
* paranoia.cpp
|
|
*
|
|
* Copyright (C) 2002-2006 Christophe Thommeret <hftom@free.fr>
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
|
|
#include <tqfile.h>
|
|
#include <tqslider.h>
|
|
#include <tqlcdnumber.h>
|
|
#include <tqdir.h>
|
|
#include <tqlineedit.h>
|
|
#include <tqbuttongroup.h>
|
|
#include <tqtoolbutton.h>
|
|
#include <tqcheckbox.h>
|
|
|
|
#include <tqcombobox.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
#include <tdelocale.h>
|
|
#include <kdebug.h>
|
|
#include <ktrader.h>
|
|
#include <kpushbutton.h>
|
|
#include <kiconloader.h>
|
|
#include <tdefiledialog.h>
|
|
#include <tdeparts/componentfactory.h>
|
|
|
|
#include "paranoia.h"
|
|
#include "paranoia.moc"
|
|
|
|
#define DEFAULT_DRIVE "/dev/cdrom"
|
|
|
|
KiloConfig::KiloConfig( TQWidget *parent, TDEConfig *confile, const TQStringList &encoders ) : ParanoiaSettings( parent )
|
|
{
|
|
int i;
|
|
|
|
TDEIconLoader *icon = new TDEIconLoader();
|
|
okBtn->setGuiItem( KGuiItem(i18n("OK"), icon->loadIconSet("ok", TDEIcon::Small) ) );
|
|
cancelBtn->setGuiItem( KGuiItem(i18n("Cancel"), icon->loadIconSet("cancel", TDEIcon::Small) ) );
|
|
baseDirBtn->setIconSet( icon->loadIconSet("document-open", TDEIcon::Small) );
|
|
delete icon;
|
|
connect( baseDirBtn, TQ_SIGNAL( clicked() ), this, TQ_SLOT( setBaseDir() ) );
|
|
Conf = confile;
|
|
Conf->setGroup( "Paranoia" );
|
|
baseDirLineEdit->setText( Conf->readEntry( "Basedir", TQDir::homeDirPath() ) );
|
|
paranoiaGroup->setButton( Conf->readNumEntry( "Mode", 0 ) );
|
|
normCb->setChecked( Conf->readBoolEntry( "Normalize", false ) );
|
|
encoderComb->insertStringList( encoders );
|
|
TQString s = Conf->readEntry( "CurrentEncoder", "" );
|
|
if ( !s.isEmpty() ) {
|
|
for ( i=0; i<(int)encoders.count(); i++ ) {
|
|
if ( encoders[i]==s ) {
|
|
encoderComb->setCurrentText( s );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void KiloConfig::setBaseDir()
|
|
{
|
|
Conf->setGroup( "Paranoia" );
|
|
TQString d = Conf->readEntry( "Basedir", TQDir::homeDirPath() );
|
|
TQString u = KFileDialog::getExistingDirectory( d );
|
|
if ( u!="" ) {
|
|
baseDirLineEdit->setText( u );
|
|
Conf->writeEntry( "Basedir", u );
|
|
}
|
|
}
|
|
|
|
TQString KiloConfig::getEncoder()
|
|
{
|
|
return encoderComb->currentText();
|
|
}
|
|
|
|
bool KiloConfig::getNormalize()
|
|
{
|
|
return normCb->isChecked();
|
|
}
|
|
|
|
TQString KiloConfig::getBaseDir()
|
|
{
|
|
return baseDirLineEdit->text().stripWhiteSpace();
|
|
}
|
|
|
|
int KiloConfig::getParanoiaMode()
|
|
{
|
|
return paranoiaGroup->selectedId();
|
|
}
|
|
|
|
void KiloConfig::accept()
|
|
{
|
|
Conf->setGroup( "Paranoia" );
|
|
Conf->writeEntry( "Mode", paranoiaGroup->id( paranoiaGroup->selected() ) );
|
|
Conf->writeEntry( "CurrentEncoder", encoderComb->currentText() );
|
|
Conf->writeEntry( "Normalize", normCb->isChecked() );
|
|
done(Accepted);
|
|
}
|
|
|
|
KiloConfig::~KiloConfig()
|
|
{
|
|
}
|
|
|
|
void paranoiaCallback( long int, paranoia_cb_mode_t )
|
|
{
|
|
}
|
|
|
|
Paranoia::Paranoia()
|
|
{
|
|
d = 0;
|
|
p = 0;
|
|
isRunning = false;
|
|
}
|
|
|
|
bool Paranoia::init( TQString dev )
|
|
{
|
|
TQString s;
|
|
TQFile f;
|
|
|
|
if ( p!=0 ) paranoia_free( p );
|
|
if (d!=0 ) cdda_close( d );
|
|
nTracks = 0;
|
|
|
|
dev = dev.stripWhiteSpace();
|
|
f.setName( dev );
|
|
if ( !f.exists() ) {
|
|
/*if ( !findCdrom() ) {
|
|
d = cdda_find_a_cdrom( CDDA_MESSAGE_PRINTIT, 0 );
|
|
if ( cdda_open( d )!=0 )
|
|
return false;
|
|
}*/
|
|
return false;
|
|
}
|
|
else {
|
|
d = cdda_identify( dev.ascii(), CDDA_MESSAGE_PRINTIT, 0 );
|
|
if ( cdda_open( d )!=0 )
|
|
return false;
|
|
}
|
|
p = paranoia_init( d );
|
|
nTracks = cdda_tracks( d );
|
|
return true;
|
|
}
|
|
|
|
bool Paranoia::findCdrom()
|
|
{
|
|
TQFile *f;
|
|
TQString c;
|
|
TQString s="";
|
|
int pos, i;
|
|
bool stop=false;
|
|
char dev[4][4]={"","","",""};
|
|
|
|
f = new TQFile( "/proc/sys/dev/cdrom/info" );
|
|
if ( !f->open(IO_ReadOnly) )
|
|
return false;
|
|
|
|
TQTextStream t( f );
|
|
while ( !t.eof() && !stop ) {
|
|
s = t.readLine();
|
|
if ( s.contains("drive name:") )
|
|
stop = true;
|
|
}
|
|
if ( !stop )
|
|
return false;
|
|
|
|
pos = s.find(":");
|
|
c = s.right( s.length()-pos-1 );
|
|
sscanf( c.latin1(), "%s %s %s %s", dev[0], dev[1], dev[2], dev[3] );
|
|
|
|
for ( i=0; i<4; i++ )
|
|
if ( procCdrom( dev[i] ) )
|
|
return true;
|
|
|
|
f->close();
|
|
return false;
|
|
}
|
|
|
|
bool Paranoia::procCdrom( TQString name )
|
|
{
|
|
int pos;
|
|
|
|
if ( name.contains("sr") ) {
|
|
pos = name.find("r");
|
|
name = name.right( name.length()-pos-1 );
|
|
name = "/dev/scd"+name;
|
|
d = cdda_identify( name.ascii(), CDDA_MESSAGE_PRINTIT, 0 );
|
|
if ( cdda_open( d )==0 )
|
|
return true;
|
|
}
|
|
else if ( name.contains("hd") ) {
|
|
name = "/dev/"+name;
|
|
d = cdda_identify( name.ascii(), CDDA_MESSAGE_PRINTIT, 0 );
|
|
if ( cdda_open( d )==0 )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Paranoia::setMode( int mode )
|
|
{
|
|
switch ( mode ) {
|
|
case 0 : mode = PARANOIA_MODE_DISABLE;
|
|
break;
|
|
case 1 : mode = PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP;
|
|
break;
|
|
case 2 : mode = PARANOIA_MODE_FULL;
|
|
}
|
|
paranoia_modeset( p, mode );
|
|
}
|
|
|
|
bool Paranoia::encode( const TQStringList &list, TQWidget *parent )
|
|
{
|
|
TQStringList desktop;
|
|
TQStringList encoderName;
|
|
|
|
encodingList.clear();
|
|
encodingList = list;
|
|
myParent = parent;
|
|
|
|
// check for encoders
|
|
TDETrader::OfferList offers = TDETrader::self()->query("KaffeineAudioEncoder");
|
|
TDETrader::OfferList::Iterator end(offers.end());
|
|
for(TDETrader::OfferList::Iterator it = offers.begin(); it != end; ++it) {
|
|
KService::Ptr ptr = (*it);
|
|
desktop.append( ptr->desktopEntryName() );
|
|
encoderName.append( ptr->name() );
|
|
}
|
|
|
|
if ( !encoderName.count() ) {
|
|
KMessageBox::error( myParent, i18n("No audio encoders could be found."), i18n("Warning") );
|
|
return false;
|
|
}
|
|
|
|
KiloConfig dlg( myParent, TDEGlobal::config(), encoderName );
|
|
int ret = dlg.exec();
|
|
if ( ret!=TQDialog::Accepted )
|
|
return false;
|
|
normalize = dlg.getNormalize();
|
|
baseDir = dlg.getBaseDir();
|
|
paraMode = dlg.getParanoiaMode();
|
|
|
|
TQString s = dlg.getEncoder();
|
|
for ( ret=0; ret<(int)encoderName.count(); ++ret ) {
|
|
if ( encoderName[ret]==s ) {
|
|
encoderDesktop = desktop[ret];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !loadEncoder( myParent ) )
|
|
return false;
|
|
|
|
if ( !currentEncoder->options( myParent, TDEGlobal::config() ) ) {
|
|
unloadEncoder();
|
|
return false;
|
|
}
|
|
|
|
if ( !setPath( baseDir, TQString(encodingList[0]).replace("/","_"), TQString(encodingList[1]).replace("/","_") ) ) {
|
|
return false;
|
|
}
|
|
isRunning = true;
|
|
start();
|
|
return true;
|
|
}
|
|
|
|
bool Paranoia::loadEncoder( TQWidget *parent )
|
|
{
|
|
int error = 0;
|
|
|
|
KService::Ptr service = KService::serviceByDesktopName( encoderDesktop );
|
|
if (!service) {
|
|
KMessageBox::error( parent, i18n("Loading of encoder '%1' failed.").arg(encoderDesktop) );
|
|
return false;
|
|
}
|
|
|
|
if ( service->serviceTypes().contains("KaffeineAudioEncoder") ) {
|
|
currentEncoder = KParts::ComponentFactory::createPartInstanceFromService<KaffeineAudioEncoder>(service, 0, service->name().ascii(), 0, 0, 0, &error);
|
|
if (error > 0) {
|
|
KMessageBox::error( parent, i18n("Loading of encoder '%1' failed.").arg(encoderDesktop) );
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void Paranoia::unloadEncoder()
|
|
{
|
|
//kdDebug()<<"Unload encoder ..."<<endl;
|
|
KService::Ptr service = KService::serviceByDesktopName( encoderDesktop );
|
|
KLibLoader::self()->unloadLibrary( service->library().ascii() );
|
|
//kdDebug()<<"... encoder unloaded."<<endl;
|
|
}
|
|
|
|
bool Paranoia::validPath( TQString path )
|
|
{
|
|
TQDir dir;
|
|
|
|
dir.setPath( path );
|
|
if ( !dir.exists() ) {
|
|
if ( !dir.mkdir( path ) ) {
|
|
KMessageBox::error( 0, i18n("Unable to create folder: ")+path );
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Paranoia::setPath( TQString &path, const TQString &artist, const TQString &album )
|
|
{
|
|
TQString s;
|
|
|
|
if ( !path.endsWith("/") )
|
|
path = path+"/";
|
|
if ( !validPath( path ) )
|
|
return false;
|
|
|
|
s = artist;
|
|
if ( s!="" )
|
|
path = path+s+"/";
|
|
if ( !validPath( path ) )
|
|
return false;
|
|
|
|
s = album;
|
|
if ( s!="" )
|
|
path = path+s+"/";
|
|
if ( !validPath( path ) )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool Paranoia::initTrack( int t )
|
|
{
|
|
currentSector = cdda_track_firstsector( d, t );
|
|
endOfTrack = cdda_track_lastsector( d, t );
|
|
paranoia_seek( p, currentSector, SEEK_SET );
|
|
return true;
|
|
}
|
|
|
|
static inline signed short paraSwap16( signed short x ) {
|
|
return ((((unsigned short)x & 0x00ffU) << 8) |
|
|
(((unsigned short )x & 0xff00U) >> 8));
|
|
}
|
|
|
|
void Paranoia::run()
|
|
{
|
|
signed short *buf;
|
|
int i, n, len, retlen;
|
|
long curpos, endpos;
|
|
TQFile f, fn;
|
|
float max;
|
|
float factor;
|
|
TQString s;
|
|
char *encoded;
|
|
int overallSectors=0;
|
|
int sectorCount=0;
|
|
|
|
progress = 0;
|
|
sleep(2); // give some time for the player to be stopped
|
|
|
|
setMode( paraMode );
|
|
for ( i=2; i<(int)encodingList.count(); ++i ) {
|
|
n = encodingList[i].left(2).toInt();
|
|
overallSectors+= trackSectorSize( n );
|
|
}
|
|
|
|
fn.setName( baseDir+".temp" );
|
|
|
|
for ( i=2; i<(int)encodingList.count(); ++i ) {
|
|
n = encodingList[i].left(2).toInt();
|
|
s = TQString(encodingList[i]).replace("/","_")+currentEncoder->getExtension();
|
|
f.setName( baseDir+s );
|
|
initTrack( n );
|
|
max = 0;
|
|
curpos = currentSector;
|
|
endpos = endOfTrack;
|
|
if ( normalize ) {
|
|
len = CDIO_CD_FRAMESIZE_RAW;
|
|
fn.open( IO_ReadWrite | IO_Truncate );
|
|
do {
|
|
buf = paranoia_read_limited( p, paranoiaCallback, 3 );
|
|
if ( TQ_BYTE_ORDER == TQ_BIG_ENDIAN ) {
|
|
for ( i=0; i<len/2; i++)
|
|
buf[i] = paraSwap16(buf[i]);
|
|
}
|
|
++curpos;
|
|
if ( len>0 ) {
|
|
for ( n=0; n<len/2; ++n )
|
|
if ( fabs(buf[n])>max )
|
|
max = fabs(buf[n]);
|
|
fn.writeBlock( (char*)buf, len );
|
|
++sectorCount;
|
|
progress = sectorCount*50/overallSectors;
|
|
}
|
|
if ( !isRunning )
|
|
len=0;
|
|
}
|
|
while ( curpos<endpos && len!=0 );
|
|
|
|
factor = 32767.0/max;
|
|
buf = new signed short[CDIO_CD_FRAMESIZE_RAW];
|
|
fn.at( 0 );
|
|
f.open( IO_ReadWrite | IO_Truncate );
|
|
currentEncoder->start( encodingList[i].remove(0,3), encodingList[0], encodingList[1], encodingList[i].left(2) );
|
|
encoded = currentEncoder->getHeader( len );
|
|
if ( encoded )
|
|
f.writeBlock( encoded, len );
|
|
|
|
do {
|
|
len = fn.readBlock( (char*)buf, CDIO_CD_FRAMESIZE_RAW );
|
|
if ( len>0 ) {
|
|
if ( max<32760 )
|
|
for ( n=0; n<len/2; ++n )
|
|
buf[n] = (float)buf[n]*factor;
|
|
encoded = currentEncoder->encode( (char*)buf, len, retlen );
|
|
if ( encoded )
|
|
f.writeBlock( encoded, retlen );
|
|
++sectorCount;
|
|
progress = sectorCount*50/overallSectors;
|
|
if ( !isRunning )
|
|
len=0;
|
|
}
|
|
}
|
|
while ( len>0 );
|
|
encoded = currentEncoder->stop( len );
|
|
if ( encoded )
|
|
f.writeBlock( encoded, len );
|
|
delete [] buf;
|
|
fn.remove();
|
|
}
|
|
else {
|
|
f.open( IO_ReadWrite | IO_Truncate );
|
|
currentEncoder->start( encodingList[i].remove(0,3), encodingList[0], encodingList[1], encodingList[i].left(2) );
|
|
encoded = currentEncoder->getHeader( len );
|
|
if ( encoded )
|
|
f.writeBlock( encoded, len );
|
|
len = CDIO_CD_FRAMESIZE_RAW;
|
|
do {
|
|
buf = paranoia_read_limited( p, paranoiaCallback, 3 );
|
|
if ( TQ_BYTE_ORDER == TQ_BIG_ENDIAN ) {
|
|
for ( i=0; i<len/2; i++) {
|
|
buf[i] = paraSwap16(buf[i]);
|
|
}
|
|
}
|
|
++curpos;
|
|
if ( len>0 ) {
|
|
encoded = currentEncoder->encode( (char*)buf, len, retlen );
|
|
if ( encoded )
|
|
f.writeBlock( encoded, retlen );
|
|
++sectorCount;
|
|
progress = sectorCount*100/overallSectors;
|
|
}
|
|
if ( !isRunning )
|
|
len=0;
|
|
}
|
|
while ( curpos<endpos && len!=0 );
|
|
encoded = currentEncoder->stop( len );
|
|
if ( encoded )
|
|
f.writeBlock( encoded, len );
|
|
sleep(1); // cdparanoia seems to like that.
|
|
}
|
|
f.flush();
|
|
f.close();
|
|
}
|
|
|
|
unloadEncoder();
|
|
isRunning = false;
|
|
}
|
|
|
|
int Paranoia::trackFirstSector( int t )
|
|
{
|
|
return cdda_track_firstsector( d, t );
|
|
}
|
|
|
|
int Paranoia::discFirstSector()
|
|
{
|
|
return cdda_disc_firstsector( d );
|
|
}
|
|
|
|
int Paranoia::discLastSector()
|
|
{
|
|
return cdda_disc_lastsector( d );
|
|
}
|
|
|
|
bool Paranoia::isAudio( int t )
|
|
{
|
|
if ( cdda_track_audiop( d, t+1 ) ) return true;
|
|
else return false;
|
|
}
|
|
|
|
TQString Paranoia::trackSize( int t )
|
|
{
|
|
TQString s, c;
|
|
long total;
|
|
|
|
total = CDIO_CD_FRAMESIZE_RAW * (cdda_track_lastsector( d, t+1 )-cdda_track_firstsector( d, t+1 ) );
|
|
if ( total>(1048576 ) ) s = c.setNum(total/1048576.0, 'f', 2)+" "+i18n("MB");
|
|
else if ( total>1024 ) s = c.setNum(total/1024.0, 'f', 2)+" "+i18n("KB");
|
|
else s = c.setNum(total*1.0, 'f', 2)+" "+i18n("Bytes");
|
|
return s;
|
|
}
|
|
|
|
long Paranoia::trackSectorSize( int t )
|
|
{
|
|
return cdda_track_lastsector( d, t )-cdda_track_firstsector( d, t );
|
|
}
|
|
|
|
TQString Paranoia::trackTime( int t )
|
|
{
|
|
TQString c;
|
|
long total, time;
|
|
int m, s;
|
|
|
|
if ( t<0 ) total = CDIO_CD_FRAMESIZE_RAW * (cdda_disc_lastsector( d )-cdda_disc_firstsector( d ) );
|
|
else total = CDIO_CD_FRAMESIZE_RAW * (cdda_track_lastsector( d, t+1 )-cdda_track_firstsector( d, t+1 ) );
|
|
time = (8 * total) / (44100 * 2 * 16);
|
|
m = time/60;
|
|
s = time%60;
|
|
c.sprintf( "%.2i:%.2i", m, s );
|
|
return c;
|
|
}
|
|
|
|
Paranoia::~Paranoia()
|
|
{
|
|
if ( p!=0 ) paranoia_free( p );
|
|
if (d!=0 ) cdda_close( d );
|
|
}
|
|
|
|
long Paranoia::getTracks()
|
|
{
|
|
return nTracks;
|
|
}
|