File indexing completed on 2024-05-12 04:51:01

0001 /*
0002     SPDX-FileCopyrightText: 1998-2010 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 
0007 #include "k3baudiojob.h"
0008 
0009 #include "k3baudioimager.h"
0010 #include "k3baudiodoc.h"
0011 #include "k3baudiotrack.h"
0012 #include "k3baudiodatasource.h"
0013 #include "k3baudionormalizejob.h"
0014 #include "k3baudiojobtempdata.h"
0015 #include "k3baudiomaxspeedjob.h"
0016 #include "k3baudiocdtracksource.h"
0017 #include "k3baudiofile.h"
0018 #include "k3bdevicemanager.h"
0019 #include "k3bdevicehandler.h"
0020 #include "k3bdevice.h"
0021 #include "k3bcdtext.h"
0022 #include "k3bmsf.h"
0023 #include "k3bglobals.h"
0024 #include "k3bexternalbinmanager.h"
0025 #include "k3bcore.h"
0026 #include "k3bcdrecordwriter.h"
0027 #include "k3bcdrdaowriter.h"
0028 #include "k3btocfilewriter.h"
0029 #include "k3binffilewriter.h"
0030 #include "k3bglobalsettings.h"
0031 #include "k3b_i18n.h"
0032 
0033 #include <KStringHandler>
0034 
0035 #include <QDebug>
0036 #include <QFile>
0037 
0038 
0039 
0040 static QString createNonExistingFilesString( const QList<K3b::AudioFile*>& items, int max )
0041 {
0042     QString s;
0043     int cnt = 0;
0044     for( QList<K3b::AudioFile*>::const_iterator it = items.begin();
0045          it != items.end(); ++it ) {
0046 
0047         s += KStringHandler::csqueeze( (*it)->filename(), 60 );
0048 
0049         ++cnt;
0050         if( cnt >= max || it == items.end() )
0051             break;
0052 
0053         s += "<br>";
0054     }
0055 
0056     if( items.count() > max )
0057         s += "...";
0058 
0059     return s;
0060 }
0061 
0062 
0063 
0064 class K3b::AudioJob::Private
0065 {
0066 public:
0067     Private()
0068         : copies(1),
0069           copiesDone(0) {
0070     }
0071 
0072     int copies;
0073     int copiesDone;
0074     int usedSpeed;
0075 
0076     bool useCdText;
0077     bool maxSpeed;
0078 
0079     bool zeroPregap;
0080     bool less4Sec;
0081 };
0082 
0083 
0084 K3b::AudioJob::AudioJob( K3b::AudioDoc* doc, K3b::JobHandler* hdl, QObject* parent )
0085     : K3b::BurnJob( hdl, parent ),
0086       m_doc( doc ),
0087       m_normalizeJob(0),
0088       m_maxSpeedJob(0)
0089 {
0090     d = new Private;
0091 
0092     m_tempData = new K3b::AudioJobTempData( m_doc, this );
0093     m_audioImager = new K3b::AudioImager( m_doc, m_tempData, this, this );
0094     connect( m_audioImager, SIGNAL(infoMessage(QString,int)),
0095              this, SIGNAL(infoMessage(QString,int)) );
0096     connect( m_audioImager, SIGNAL(percent(int)),
0097              this, SLOT(slotAudioDecoderPercent(int)) );
0098     connect( m_audioImager, SIGNAL(subPercent(int)),
0099              this, SLOT(slotAudioDecoderSubPercent(int)) );
0100     connect( m_audioImager, SIGNAL(finished(bool)),
0101              this, SLOT(slotAudioDecoderFinished(bool)) );
0102     connect( m_audioImager, SIGNAL(nextTrack(int,int)),
0103              this, SLOT(slotAudioDecoderNextTrack(int,int)) );
0104 
0105     m_writer = 0;
0106 }
0107 
0108 
0109 K3b::AudioJob::~AudioJob()
0110 {
0111     delete d;
0112 }
0113 
0114 
0115 K3b::Device::Device* K3b::AudioJob::writer() const
0116 {
0117     if( m_doc->onlyCreateImages() )
0118         return 0; // no writer needed -> no blocking on K3b::BurnJob
0119     else
0120         return m_doc->burner();
0121 }
0122 
0123 
0124 K3b::Doc* K3b::AudioJob::doc() const
0125 {
0126     return m_doc;
0127 }
0128 
0129 
0130 void K3b::AudioJob::start()
0131 {
0132     jobStarted();
0133 
0134     m_written = true;
0135     m_canceled = false;
0136     m_errorOccuredAndAlreadyReported = false;
0137     d->copies = m_doc->copies();
0138     d->copiesDone = 0;
0139     d->useCdText = m_doc->cdText();
0140     d->usedSpeed = m_doc->speed();
0141     d->maxSpeed = false;
0142 
0143     if( m_doc->dummy() )
0144         d->copies = 1;
0145 
0146     emit newTask( i18n("Preparing data") );
0147     const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
0148     //
0149     // Check if all files exist
0150     //
0151     QList<K3b::AudioFile*> nonExistingFiles;
0152     K3b::AudioTrack* track = m_doc->firstTrack();
0153     while( track ) {
0154         K3b::AudioDataSource* source = track->firstSource();
0155         while( source ) {
0156             if( K3b::AudioFile* file = dynamic_cast<K3b::AudioFile*>( source ) ) {
0157                 if( !QFile::exists( file->filename() ) )
0158                     nonExistingFiles.append( file );
0159             }
0160             source = source->next();
0161         }
0162         track = track->next();
0163     }
0164     if( !nonExistingFiles.isEmpty() ) {
0165         if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the "
0166                                         "project and continue without adding them to the image?") +
0167                            "<p>" + createNonExistingFilesString( nonExistingFiles, 10 ),
0168                            i18n("Warning"),
0169                            KGuiItem( i18n("Remove missing files and continue") ),
0170                            KGuiItem( i18n("Cancel and go back") ) ) ) {
0171             for( QList<K3b::AudioFile*>::const_iterator it = nonExistingFiles.constBegin();
0172                  it != nonExistingFiles.constEnd(); ++it ) {
0173                 delete *it;
0174             }
0175         }
0176         else {
0177             m_canceled = true;
0178             emit canceled();
0179             jobFinished(false);
0180             return;
0181         }
0182     }
0183 
0184     //
0185     // Make sure the project is not empty
0186     //
0187     if( m_doc->numOfTracks() == 0 ) {
0188         emit infoMessage( i18n("Please add files to your project first."), MessageError );
0189         jobFinished(false);
0190         return;
0191     }
0192 
0193     if( m_doc->onTheFly() && !checkAudioSources() ) {
0194         emit infoMessage( i18n("Unable to write on-the-fly with these audio sources."), MessageWarning );
0195         m_doc->setOnTheFly(false);
0196     }
0197 
0198 
0199     // we don't need this when only creating image and it is possible
0200     // that the burn device is null
0201     if( !m_doc->onlyCreateImages() ) {
0202 
0203         //
0204         // there are a lot of writers out there which produce coasters
0205         // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?)
0206         //
0207         // Also most writers do not accept cuesheets with tracks smaller than 4 seconds (a violation
0208         // of the red book standard) in DAO mode.
0209         //
0210         d->zeroPregap = false;
0211         d->less4Sec = false;
0212         track = m_doc->firstTrack();
0213         while( track ) {
0214             if( track->postGap() == 0 && track->next() != 0 ) // the last track's postgap is always 0
0215                 d->zeroPregap = true;
0216 
0217             if( track->length() < K3b::Msf( 0, 4, 0 ) )
0218                 d->less4Sec = true;
0219 
0220             track = track->next();
0221         }
0222 
0223         // determine writing mode
0224         if( m_doc->writingMode() == K3b::WritingModeAuto ) {
0225             //
0226             // DAO is always the first choice
0227             // RAW second and TAO last
0228             // there are none-DAO writers that are supported by cdrdao
0229             //
0230             // older cdrecord versions do not support the -shorttrack option in RAW writing mode
0231             //
0232             if( !writer()->dao() && writingApp() == K3b::WritingAppCdrecord ) {
0233                 if(!writer()->supportsRawWriting() &&
0234                    ( !d->less4Sec || ( cdrecordBin && cdrecordBin->hasFeature( "short-track-raw" ) ) ) )
0235                     m_usedWritingMode = K3b::WritingModeRaw;
0236                 else
0237                     m_usedWritingMode = K3b::WritingModeTao;
0238             }
0239             else {
0240                 if( (d->zeroPregap||d->less4Sec) && writer()->supportsRawWriting() ) {
0241                     m_usedWritingMode = K3b::WritingModeRaw;
0242                     if( d->less4Sec )
0243                         emit infoMessage( i18n("Track lengths below 4 seconds violate the Red Book standard."), MessageWarning );
0244                 }
0245                 else
0246                     m_usedWritingMode = K3b::WritingModeSao;
0247             }
0248         }
0249         else
0250             m_usedWritingMode = m_doc->writingMode();
0251 
0252         bool cdrecordOnTheFly = false;
0253         bool cdrecordCdText = false;
0254         if( cdrecordBin ) {
0255             cdrecordOnTheFly = cdrecordBin->hasFeature( "audio-stdin" );
0256             cdrecordCdText = cdrecordBin->hasFeature( "cdtext" );
0257         }
0258 
0259         // determine writing app
0260         if( writingApp() == K3b::WritingAppAuto ) {
0261             if( m_usedWritingMode == K3b::WritingModeSao ) {
0262                 // there are none-DAO writers that are supported by cdrdao
0263                 if( !writer()->dao() ||
0264                     ( !cdrecordOnTheFly && m_doc->onTheFly() ) ||
0265                     ( d->useCdText && !cdrecordCdText ) ||
0266                     m_doc->hideFirstTrack() )
0267                     m_usedWritingApp = K3b::WritingAppCdrdao;
0268                 else
0269                     m_usedWritingApp = K3b::WritingAppCdrecord;
0270             }
0271             else
0272                 m_usedWritingApp = K3b::WritingAppCdrecord;
0273         }
0274         else
0275             m_usedWritingApp = writingApp();
0276 
0277         // on-the-fly writing with cdrecord >= 2.01a13
0278         if( m_usedWritingApp == K3b::WritingAppCdrecord &&
0279             m_doc->onTheFly() &&
0280             !cdrecordOnTheFly ) {
0281             emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), MessageError );
0282             m_doc->setOnTheFly(false);
0283         }
0284 
0285         if( m_usedWritingApp == K3b::WritingAppCdrecord &&
0286             d->useCdText ) {
0287             if( !cdrecordCdText ) {
0288                 emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.",
0289                                        cdrecordBin->version()), MessageError );
0290                 d->useCdText = false;
0291             }
0292             else if( m_usedWritingMode == K3b::WritingModeTao ) {
0293                 emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), MessageWarning );
0294                 d->useCdText = false;
0295             }
0296         }
0297     }
0298 
0299 
0300     if( !m_doc->onlyCreateImages() && m_doc->onTheFly() ) {
0301         if( m_doc->speed() == 0 ) {
0302             // try to determine the max possible speed
0303             emit newSubTask( i18n("Determining maximum writing speed") );
0304             if( !m_maxSpeedJob ) {
0305                 m_maxSpeedJob = new K3b::AudioMaxSpeedJob( m_doc, this, this );
0306                 connect( m_maxSpeedJob, SIGNAL(percent(int)),
0307                          this, SIGNAL(subPercent(int)) );
0308                 connect( m_maxSpeedJob, SIGNAL(finished(bool)),
0309                          this, SLOT(slotMaxSpeedJobFinished(bool)) );
0310             }
0311             m_maxSpeedJob->start();
0312             return;
0313         }
0314         else {
0315             if( !prepareWriter() ) {
0316                 cleanupAfterError();
0317                 jobFinished(false);
0318                 return;
0319             }
0320 
0321             if( startWriting() ) {
0322 
0323                 // now the writer is running and we can get it's stdin
0324                 // we only use this method when writing on-the-fly since
0325                 // we cannot easily change the audioDecode device while it's working
0326                 // which we would need to do since we write into several
0327                 // image files.
0328                 m_audioImager->writeTo( m_writer->ioDevice() );
0329             }
0330             else {
0331                 // startWriting() already did the cleanup
0332                 return;
0333             }
0334         }
0335     }
0336     else {
0337         emit burning(false);
0338         emit infoMessage( i18n("Creating image files in %1", m_doc->tempDir()), MessageInfo );
0339         emit newTask( i18n("Creating image files") );
0340         m_tempData->prepareTempFileNames( doc()->tempDir() );
0341     }
0342 
0343     m_audioImager->start();
0344 }
0345 
0346 
0347 void K3b::AudioJob::slotMaxSpeedJobFinished( bool success )
0348 {
0349     d->maxSpeed = success;
0350     if( !success )
0351         emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), MessageWarning );
0352 
0353     // now start the writing
0354     // same code as above. See the comments there
0355     if( !prepareWriter() ) {
0356         cleanupAfterError();
0357         jobFinished(false);
0358         return;
0359     }
0360 
0361     if( startWriting() )
0362         m_audioImager->writeTo( m_writer->ioDevice() );
0363 
0364     m_audioImager->start();
0365 }
0366 
0367 
0368 void K3b::AudioJob::cancel()
0369 {
0370     m_canceled = true;
0371 
0372     if( m_maxSpeedJob )
0373         m_maxSpeedJob->cancel();
0374 
0375     if( m_writer )
0376         m_writer->cancel();
0377 
0378     m_audioImager->cancel();
0379     emit infoMessage( i18n("Writing canceled."), K3b::Job::MessageError );
0380     removeBufferFiles();
0381     emit canceled();
0382     jobFinished(false);
0383 }
0384 
0385 
0386 void K3b::AudioJob::slotWriterFinished( bool success )
0387 {
0388     if( m_canceled || m_errorOccuredAndAlreadyReported )
0389         return;
0390 
0391     if( !success ) {
0392         cleanupAfterError();
0393         jobFinished(false);
0394         return;
0395     }
0396     else {
0397         d->copiesDone++;
0398 
0399         if( d->copiesDone == d->copies ) {
0400             if( m_doc->onTheFly() || m_doc->removeImages() )
0401                 removeBufferFiles();
0402 
0403             if ( k3bcore->globalSettings()->ejectMedia() ) {
0404                 K3b::Device::eject( m_doc->burner() );
0405             }
0406 
0407             jobFinished(true);
0408         }
0409         else {
0410             if( !K3b::eject( m_doc->burner() ) ) {
0411                 blockingInformation( i18n("K3b was unable to eject the written disk. Please do so manually.") );
0412             }
0413 
0414             if( startWriting() ) {
0415                 if( m_doc->onTheFly() ) {
0416                     // now the writer is running and we can get it's stdin
0417                     // we only use this method when writing on-the-fly since
0418                     // we cannot easily change the audioDecode fd while it's working
0419                     // which we would need to do since we write into several
0420                     // image files.
0421                     m_audioImager->writeTo( m_writer->ioDevice() );
0422                     m_audioImager->start();
0423                 }
0424             }
0425         }
0426     }
0427 }
0428 
0429 
0430 void K3b::AudioJob::slotAudioDecoderFinished( bool success )
0431 {
0432     if( m_canceled || m_errorOccuredAndAlreadyReported )
0433         return;
0434 
0435     if( !success ) {
0436         if( m_audioImager->lastErrorType() == K3b::AudioImager::ERROR_FD_WRITE ) {
0437             // this means that the writer job failed so let's use the error handling there.
0438             return;
0439         }
0440 
0441         emit infoMessage( i18n("Error while decoding audio tracks."), MessageError );
0442         cleanupAfterError();
0443         jobFinished(false);
0444         return;
0445     }
0446 
0447     if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
0448 
0449         emit infoMessage( i18n("Successfully decoded all tracks."), MessageSuccess );
0450 
0451         if( m_doc->normalize() ) {
0452             normalizeFiles();
0453         }
0454         else if( !m_doc->onlyCreateImages() ) {
0455             if( !prepareWriter() ) {
0456                 cleanupAfterError();
0457                 jobFinished(false);
0458             }
0459             else
0460                 startWriting();
0461         }
0462         else {
0463             jobFinished(true);
0464         }
0465     }
0466 }
0467 
0468 
0469 void K3b::AudioJob::slotAudioDecoderNextTrack( int t, int tt )
0470 {
0471     if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
0472         K3b::AudioTrack* track = m_doc->getTrack(t);
0473         emit newSubTask( i18n("Decoding audio track %1 of %2%3",
0474                               t,
0475                               tt,
0476                               track->title().isEmpty() || track->artist().isEmpty()
0477                               ? QString()
0478                               : " (" + track->artist() + " - " + track->title() + ')' ) );
0479     }
0480 }
0481 
0482 
0483 bool K3b::AudioJob::prepareWriter()
0484 {
0485     delete m_writer;
0486 
0487     if( m_usedWritingApp == K3b::WritingAppCdrecord ) {
0488 
0489         if( !writeInfFiles() ) {
0490             qDebug() << "(K3b::AudioJob) could not write inf-files.";
0491             emit infoMessage( i18n("I/O Error. Most likely no space left on harddisk."), MessageError );
0492 
0493             return false;
0494         }
0495 
0496         K3b::CdrecordWriter* writer = new K3b::CdrecordWriter( m_doc->burner(), this, this );
0497 
0498         writer->setWritingMode( m_usedWritingMode );
0499         writer->setSimulate( m_doc->dummy() );
0500         writer->setBurnSpeed( d->usedSpeed );
0501 
0502         writer->addArgument( "-useinfo" );
0503 
0504         if( d->useCdText ) {
0505             writer->setRawCdText( m_doc->cdTextData().rawPackData() );
0506         }
0507 
0508         // add all the audio tracks
0509         writer->addArgument( "-audio" );
0510         const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
0511         // we only need to pad in one case. cdrecord < 2.01.01a03 cannot handle shorttrack + raw
0512         if( d->less4Sec ) {
0513             if( m_usedWritingMode == K3b::WritingModeRaw && ( cdrecordBin &&
0514                 !cdrecordBin->hasFeature( "short-track-raw" ) ) ) {
0515                 writer->addArgument( "-pad" );
0516             }
0517             else {
0518                 // Allow tracks shorter than 4 seconds
0519                 writer->addArgument( "-shorttrack" );
0520             }
0521         }
0522 
0523         K3b::AudioTrack* track = m_doc->firstTrack();
0524         while( track ) {
0525             if( m_doc->onTheFly() ) {
0526                 // this is only supported by cdrecord versions >= 2.01a13
0527                 writer->addArgument( QFile::encodeName( m_tempData->infFileName( track ) ) );
0528             }
0529             else {
0530                 writer->addArgument( QFile::encodeName( m_tempData->bufferFileName( track ) ) );
0531             }
0532             track = track->next();
0533         }
0534 
0535         m_writer = writer;
0536     }
0537     else {
0538         if( !writeTocFile() ) {
0539             qDebug() << "(K3b::DataJob) could not write tocfile.";
0540             emit infoMessage( i18n("I/O Error"), MessageError );
0541 
0542             return false;
0543         }
0544 
0545         // create the writer
0546         // create cdrdao job
0547         K3b::CdrdaoWriter* writer = new K3b::CdrdaoWriter( m_doc->burner(), this, this );
0548         writer->setCommand( K3b::CdrdaoWriter::WRITE );
0549         writer->setSimulate( m_doc->dummy() );
0550         writer->setBurnSpeed( d->usedSpeed );
0551         writer->setTocFile( m_tempData->tocFileName() );
0552 
0553         m_writer = writer;
0554     }
0555 
0556     connect( m_writer, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0557     connect( m_writer, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) );
0558     connect( m_writer, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSize(int,int)) );
0559     connect( m_writer, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
0560     connect( m_writer, SIGNAL(processedSubSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
0561     connect( m_writer, SIGNAL(nextTrack(int,int)), this, SLOT(slotWriterNextTrack(int,int)) );
0562     connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
0563     connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
0564     connect( m_writer, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
0565     connect( m_writer, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
0566     //  connect( m_writer, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) );
0567     connect( m_writer, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) );
0568     connect( m_writer, SIGNAL(debuggingOutput(QString,QString)),
0569              this, SIGNAL(debuggingOutput(QString,QString)) );
0570 
0571     return true;
0572 }
0573 
0574 
0575 void K3b::AudioJob::slotWriterNextTrack( int t, int tt )
0576 {
0577     K3b::AudioTrack* track = m_doc->getTrack(t);
0578     // t is in range 1..tt
0579     if( m_doc->hideFirstTrack() )
0580         track = m_doc->getTrack(t+1);
0581     emit newSubTask( i18n("Writing track %1 of %2%3",
0582                           t,
0583                           tt,
0584                           track->title().isEmpty() || track->artist().isEmpty()
0585                           ? QString()
0586                           : " (" + track->artist() + " - " + track->title() + ')' ) );
0587 }
0588 
0589 
0590 void K3b::AudioJob::slotWriterJobPercent( int p )
0591 {
0592     double totalTasks = d->copies;
0593     double tasksDone = d->copiesDone;
0594     if( m_doc->normalize() ) {
0595         totalTasks+=1.0;
0596         tasksDone+=1.0;
0597     }
0598     if( !m_doc->onTheFly() ) {
0599         totalTasks+=1.0;
0600         tasksDone+=1.0;
0601     }
0602 
0603     emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0604 }
0605 
0606 
0607 void K3b::AudioJob::slotAudioDecoderPercent( int p )
0608 {
0609     if( m_doc->onlyCreateImages() ) {
0610         if( m_doc->normalize() )
0611             emit percent( p/2 );
0612         else
0613             emit percent( p );
0614     }
0615     else if( !m_doc->onTheFly() ) {
0616         double totalTasks = d->copies;
0617         double tasksDone = d->copiesDone; // =0 when creating an image
0618         if( m_doc->normalize() ) {
0619             totalTasks+=1.0;
0620         }
0621         if( !m_doc->onTheFly() ) {
0622             totalTasks+=1.0;
0623         }
0624 
0625         emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0626     }
0627 }
0628 
0629 
0630 void K3b::AudioJob::slotAudioDecoderSubPercent( int p )
0631 {
0632     // when writing on the fly the writer produces the subPercent
0633     if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
0634         emit subPercent( p );
0635     }
0636 }
0637 
0638 
0639 bool K3b::AudioJob::startWriting()
0640 {
0641     if( m_doc->dummy() )
0642         emit newTask( i18n("Simulating") );
0643     else if( d->copies > 1 )
0644         emit newTask( i18n("Writing Copy %1", d->copiesDone+1) );
0645     else
0646         emit newTask( i18n("Writing") );
0647 
0648 
0649     emit newSubTask( i18n("Waiting for media") );
0650     if( waitForMedium( m_doc->burner() ) == Device::MEDIA_UNKNOWN ) {
0651         cancel();
0652         return false;
0653     }
0654 
0655     // just to be sure we did not get canceled during the async discWaiting
0656     if( m_canceled )
0657         return false;
0658 
0659     // in case we determined the max possible writing speed we have to reset the speed on the writer job
0660     // here since an inserted media is necessary
0661     // the Max speed job will compare the max speed value with the supported values of the writer
0662     if( d->maxSpeed )
0663         m_writer->setBurnSpeed( m_maxSpeedJob->maxSpeed() );
0664 
0665     emit burning(true);
0666     m_writer->start();
0667     return true;
0668 }
0669 
0670 
0671 void K3b::AudioJob::cleanupAfterError()
0672 {
0673     m_errorOccuredAndAlreadyReported = true;
0674     m_audioImager->cancel();
0675 
0676     if( m_writer )
0677         m_writer->cancel();
0678 
0679     // remove the temp files
0680     removeBufferFiles();
0681 }
0682 
0683 
0684 void K3b::AudioJob::removeBufferFiles()
0685 {
0686     if ( !m_doc->onTheFly() ) {
0687         emit infoMessage( i18n("Removing temporary files."), MessageInfo );
0688     }
0689 
0690     // removes buffer images and temp toc or inf files
0691     m_tempData->cleanup();
0692 }
0693 
0694 
0695 void K3b::AudioJob::normalizeFiles()
0696 {
0697     if( !m_normalizeJob ) {
0698         m_normalizeJob = new K3b::AudioNormalizeJob( this, this );
0699 
0700         connect( m_normalizeJob, SIGNAL(infoMessage(QString,int)),
0701                  this, SIGNAL(infoMessage(QString,int)) );
0702         connect( m_normalizeJob, SIGNAL(percent(int)), this, SLOT(slotNormalizeProgress(int)) );
0703         connect( m_normalizeJob, SIGNAL(subPercent(int)), this, SLOT(slotNormalizeSubProgress(int)) );
0704         connect( m_normalizeJob, SIGNAL(finished(bool)), this, SLOT(slotNormalizeJobFinished(bool)) );
0705         connect( m_normalizeJob, SIGNAL(newTask(QString)), this, SIGNAL(newSubTask(QString)) );
0706         connect( m_normalizeJob, SIGNAL(debuggingOutput(QString,QString)),
0707                  this, SIGNAL(debuggingOutput(QString,QString)) );
0708     }
0709 
0710     // add all the files
0711     // TODO: we may need to split the wave files and put them back together!
0712     QList<QString> files;
0713     K3b::AudioTrack* track = m_doc->firstTrack();
0714     while( track ) {
0715         files.append( m_tempData->bufferFileName(track) );
0716         track = track->next();
0717     }
0718 
0719     m_normalizeJob->setFilesToNormalize( files );
0720 
0721     emit newTask( i18n("Normalizing volume levels") );
0722     m_normalizeJob->start();
0723 }
0724 
0725 void K3b::AudioJob::slotNormalizeJobFinished( bool success )
0726 {
0727     if( m_canceled || m_errorOccuredAndAlreadyReported )
0728         return;
0729 
0730     if( success ) {
0731         if( m_doc->onlyCreateImages() ) {
0732             jobFinished(true);
0733         }
0734         else {
0735             // start the writing
0736             if( !prepareWriter() ) {
0737                 cleanupAfterError();
0738                 jobFinished(false);
0739             }
0740             else
0741                 startWriting();
0742         }
0743     }
0744     else {
0745         cleanupAfterError();
0746         jobFinished(false);
0747     }
0748 }
0749 
0750 void K3b::AudioJob::slotNormalizeProgress( int p )
0751 {
0752     double totalTasks = d->copies+2.0;
0753     double tasksDone = 1; // the decoding has been finished
0754 
0755     emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0756 }
0757 
0758 
0759 void K3b::AudioJob::slotNormalizeSubProgress( int p )
0760 {
0761     emit subPercent( p );
0762 }
0763 
0764 
0765 bool K3b::AudioJob::writeTocFile()
0766 {
0767     K3b::TocFileWriter tocWriter;
0768     tocWriter.setData( m_doc->toToc() );
0769     tocWriter.setHideFirstTrack( m_doc->hideFirstTrack() );
0770     if( d->useCdText )
0771         tocWriter.setCdText( m_doc->cdTextData() );
0772     if( !m_doc->onTheFly() ) {
0773         QStringList filenames;
0774         for( int i = 1; i <= m_doc->numOfTracks(); ++i )
0775             filenames += m_tempData->bufferFileName( i );
0776         tocWriter.setFilenames( filenames );
0777     }
0778     return tocWriter.save( m_tempData->tocFileName() );
0779 }
0780 
0781 
0782 bool K3b::AudioJob::writeInfFiles()
0783 {
0784     K3b::InfFileWriter infFileWriter;
0785     K3b::AudioTrack* track = m_doc->firstTrack();
0786     while( track ) {
0787 
0788         infFileWriter.setTrack( track->toCdTrack() );
0789         infFileWriter.setTrackNumber( track->trackNumber() );
0790         if( !m_doc->onTheFly() )
0791             infFileWriter.setBigEndian( false );
0792 
0793         if( !infFileWriter.save( m_tempData->infFileName(track) ) )
0794             return false;
0795 
0796         track = track->next();
0797     }
0798     return true;
0799 }
0800 
0801 
0802 // checks if the doc contains sources from an audio cd which cannot be read on-the-fly
0803 bool K3b::AudioJob::checkAudioSources()
0804 {
0805     K3b::AudioTrack* track = m_doc->firstTrack();
0806     K3b::AudioDataSource* source = track->firstSource();
0807 
0808     while( source ) {
0809 
0810         if( K3b::AudioCdTrackSource* cdSource = dynamic_cast<K3b::AudioCdTrackSource*>(source) ) {
0811             //
0812             // If which cases we cannot wite on-the-fly:
0813             // 1. the writing device contains one of the audio cds
0814             // 2. Well, one of the cds is missing
0815             //
0816             K3b::Device::Device* dev = cdSource->searchForAudioCD();
0817             if( !dev || dev == writer() )
0818                 return false;
0819             else
0820                 cdSource->setDevice( dev );
0821         }
0822 
0823         // next source
0824         source = source->next();
0825         if( !source ) {
0826             track = track->next();
0827             if( track )
0828                 source = track->firstSource();
0829         }
0830     }
0831 
0832     return true;
0833 }
0834 
0835 
0836 QString K3b::AudioJob::jobDescription() const
0837 {
0838     return i18n("Writing Audio CD")
0839         + ( m_doc->title().isEmpty()
0840             ? QString()
0841             : QString( " (%1)" ).arg(m_doc->title()) );
0842 }
0843 
0844 
0845 QString K3b::AudioJob::jobDetails() const
0846 {
0847     return ( i18ncp("%2 is of form XX:YY:ZZ, no pluralization needed",
0848                     "1 track (%2 minutes)",
0849                     "%1 tracks (%2 minutes)",
0850                     m_doc->numOfTracks(), m_doc->length().toString())
0851              + ( m_doc->copies() > 1 && !m_doc->dummy()
0852                  ? i18np(" - %1 copy", " - %1 copies", m_doc->copies())
0853                  : QString() ) );
0854 }
0855 
0856 #include "moc_k3baudiojob.cpp"