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

0001 /*
0002     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 #include "k3bmixedjob.h"
0006 #include "k3bmixeddoc.h"
0007 #include "k3bactivepipe.h"
0008 #include "k3bfilesplitter.h"
0009 
0010 #include "k3bdatadoc.h"
0011 #include "k3bisoimager.h"
0012 #include "k3bisooptions.h"
0013 #include "k3bmsinfofetcher.h"
0014 #include "k3baudioimager.h"
0015 #include "k3baudiodoc.h"
0016 #include "k3baudiotrack.h"
0017 #include "k3baudionormalizejob.h"
0018 #include "k3baudiojobtempdata.h"
0019 #include "k3baudiomaxspeedjob.h"
0020 #include "k3bdevicemanager.h"
0021 #include "k3bdevice.h"
0022 #include "k3bdevicehandler.h"
0023 #include "k3bmsf.h"
0024 #include "k3bglobals.h"
0025 #include "k3bexternalbinmanager.h"
0026 #include "k3bversion.h"
0027 #include "k3bcore.h"
0028 #include "k3bcdrecordwriter.h"
0029 #include "k3bcdrdaowriter.h"
0030 #include "k3btocfilewriter.h"
0031 #include "k3binffilewriter.h"
0032 #include "k3bglobalsettings.h"
0033 #include "k3baudiofile.h"
0034 #include "k3b_i18n.h"
0035 
0036 #include <KStringHandler>
0037 #include <KIO/CopyJob>
0038 #include <KIO/DeleteJob>
0039 #include <KIO/Global>
0040 
0041 #include <QDataStream>
0042 #include <QDebug>
0043 #include <QFile>
0044 #include <QTemporaryFile>
0045 
0046 
0047 static QString createNonExistingFilesString( const QList<K3b::AudioFile*>& items, int max )
0048 {
0049     QString s;
0050     int cnt = 0;
0051     for( QList<K3b::AudioFile*>::const_iterator it = items.begin();
0052          it != items.end(); ++it ) {
0053 
0054         s += KStringHandler::csqueeze( (*it)->filename(), 60 );
0055 
0056         ++cnt;
0057         if( cnt >= max || it == items.end() )
0058             break;
0059 
0060         s += "<br>";
0061     }
0062 
0063     if( items.count() > max )
0064         s += "...";
0065 
0066     return s;
0067 }
0068 
0069 
0070 
0071 class K3b::MixedJob::Private
0072 {
0073 public:
0074     Private()
0075         : maxSpeedJob(0) {
0076     }
0077 
0078 
0079     int copies;
0080     int copiesDone;
0081 
0082     K3b::AudioMaxSpeedJob* maxSpeedJob;
0083     bool maxSpeed;
0084 
0085     ActivePipe pipe;
0086 
0087     FileSplitter dataImageFile;
0088 };
0089 
0090 
0091 K3b::MixedJob::MixedJob( K3b::MixedDoc* doc, K3b::JobHandler* hdl, QObject* parent )
0092     : K3b::BurnJob( hdl, parent ),
0093       m_doc( doc ),
0094       m_normalizeJob(0)
0095 {
0096     d = new Private;
0097 
0098     m_isoImager = new K3b::IsoImager( doc->dataDoc(), this, this );
0099     connect( m_isoImager, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0100     connect( m_isoImager, SIGNAL(percent(int)), this, SLOT(slotIsoImagerPercent(int)) );
0101     connect( m_isoImager, SIGNAL(finished(bool)), this, SLOT(slotIsoImagerFinished(bool)) );
0102     connect( m_isoImager, SIGNAL(debuggingOutput(QString,QString)),
0103              this, SIGNAL(debuggingOutput(QString,QString)) );
0104 
0105     m_tempData = new K3b::AudioJobTempData( m_doc->audioDoc(), this );
0106     m_audioImager = new K3b::AudioImager( doc->audioDoc(), m_tempData, this, this );
0107     connect( m_audioImager, SIGNAL(infoMessage(QString,int)),
0108              this, SIGNAL(infoMessage(QString,int)) );
0109     connect( m_audioImager, SIGNAL(percent(int)), this, SLOT(slotAudioDecoderPercent(int)) );
0110     connect( m_audioImager, SIGNAL(subPercent(int)), this, SLOT(slotAudioDecoderSubPercent(int)) );
0111     connect( m_audioImager, SIGNAL(finished(bool)), this, SLOT(slotAudioDecoderFinished(bool)) );
0112     connect( m_audioImager, SIGNAL(nextTrack(int,int)), this, SLOT(slotAudioDecoderNextTrack(int,int)) );
0113 
0114     m_msInfoFetcher = new K3b::MsInfoFetcher( this, this );
0115     connect( m_msInfoFetcher, SIGNAL(finished(bool)), this, SLOT(slotMsInfoFetched(bool)) );
0116     connect( m_msInfoFetcher, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0117 
0118     m_writer = 0;
0119     m_tocFile = 0;
0120 }
0121 
0122 
0123 K3b::MixedJob::~MixedJob()
0124 {
0125     delete m_tocFile;
0126     delete d;
0127 }
0128 
0129 
0130 K3b::Device::Device* K3b::MixedJob::writer() const
0131 {
0132     if( m_doc->onlyCreateImages() )
0133         return 0;
0134     else
0135         return m_doc->burner();
0136 }
0137 
0138 
0139 K3b::Doc* K3b::MixedJob::doc() const
0140 {
0141     return m_doc;
0142 }
0143 
0144 
0145 void K3b::MixedJob::start()
0146 {
0147     jobStarted();
0148 
0149     m_canceled = false;
0150     m_errorOccuredAndAlreadyReported = false;
0151     d->copiesDone = 0;
0152     d->copies = m_doc->copies();
0153     m_currentAction = PREPARING_DATA;
0154     d->maxSpeed = false;
0155 
0156     if( m_doc->dummy() )
0157         d->copies = 1;
0158 
0159     prepareProgressInformation();
0160 
0161     //
0162     // Check if all files exist
0163     //
0164     QList<K3b::AudioFile*> nonExistingFiles;
0165     K3b::AudioTrack* track = m_doc->audioDoc()->firstTrack();
0166     while( track ) {
0167         K3b::AudioDataSource* source = track->firstSource();
0168         while( source ) {
0169             if( K3b::AudioFile* file = dynamic_cast<K3b::AudioFile*>( source ) ) {
0170                 if( !QFile::exists( file->filename() ) )
0171                     nonExistingFiles.append( file );
0172             }
0173             source = source->next();
0174         }
0175         track = track->next();
0176     }
0177     if( !nonExistingFiles.isEmpty() ) {
0178         if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the "
0179                                         "project and continue without adding them to the image?") +
0180                            "<p>" + createNonExistingFilesString( nonExistingFiles, 10 ),
0181                            i18n("Warning"),
0182                            KGuiItem( i18n("Remove missing files and continue") ),
0183                            KGuiItem( i18n("Cancel and go back") ) ) ) {
0184             for( QList<K3b::AudioFile*>::const_iterator it = nonExistingFiles.constBegin();
0185                  it != nonExistingFiles.constEnd(); ++it ) {
0186                 delete *it;
0187             }
0188         }
0189         else {
0190             m_canceled = true;
0191             emit canceled();
0192             jobFinished(false);
0193             return;
0194         }
0195     }
0196 
0197     //
0198     // Make sure the project is not empty
0199     //
0200     if( m_doc->audioDoc()->numOfTracks() == 0 ) {
0201         emit infoMessage( i18n("Please add files to your project first."), MessageError );
0202         jobFinished(false);
0203         return;
0204     }
0205 
0206 
0207     // set some flags that are needed
0208     m_doc->audioDoc()->setOnTheFly( m_doc->onTheFly() );  // for the toc writer
0209     m_doc->audioDoc()->setHideFirstTrack( false );   // unsupported
0210     m_doc->dataDoc()->setBurner( m_doc->burner() );  // so the isoImager can read ms data
0211 
0212     emit newTask( i18n("Preparing data") );
0213 
0214     determineWritingMode();
0215 
0216     //
0217     // First we make sure the data portion is valid
0218     //
0219 
0220     // we do not have msinfo yet
0221     m_currentAction = INITIALIZING_IMAGER;
0222     m_isoImager->setMultiSessionInfo( QString() );
0223     m_isoImager->init();
0224 }
0225 
0226 
0227 void K3b::MixedJob::startFirstCopy()
0228 {
0229     //
0230     // if not onthefly create the iso image and then the wavs
0231     // and write then
0232     // if onthefly calculate the iso size
0233     //
0234     if( m_doc->onTheFly() ) {
0235         if( m_doc->speed() == 0 ) {
0236             emit newSubTask( i18n("Determining maximum writing speed") );
0237 
0238             //
0239             // try to determine the max possible speed
0240             // no need to check the data track's max speed. Most current systems are able
0241             // to handle the maximum possible
0242             //
0243             if( !d->maxSpeedJob ) {
0244                 // the maxspeed job gets the device from the doc:
0245                 m_doc->audioDoc()->setBurner( m_doc->burner() );
0246                 d->maxSpeedJob = new K3b::AudioMaxSpeedJob( m_doc->audioDoc(), this, this );
0247                 connect( d->maxSpeedJob, SIGNAL(percent(int)),
0248                          this, SIGNAL(subPercent(int)) );
0249                 connect( d->maxSpeedJob, SIGNAL(finished(bool)),
0250                          this, SLOT(slotMaxSpeedJobFinished(bool)) );
0251             }
0252             d->maxSpeedJob->start();
0253         }
0254         else if( m_doc->mixedType() != K3b::MixedDoc::DATA_SECOND_SESSION ) {
0255             m_currentAction = PREPARING_DATA;
0256             m_isoImager->calculateSize();
0257         }
0258         else {
0259             // we cannot calculate the size since we don't have the msinfo yet
0260             // so first write the audio session
0261             writeNextCopy();
0262         }
0263     }
0264     else {
0265         emit burning(false);
0266 
0267         emit infoMessage( i18n("Creating audio image files in %1",m_doc->tempDir()), MessageInfo );
0268 
0269         m_tempFilePrefix = K3b::findUniqueFilePrefix( ( !m_doc->audioDoc()->title().isEmpty()
0270                                                         ? m_doc->audioDoc()->title()
0271                                                         : m_doc->dataDoc()->isoOptions().volumeID() ),
0272                                                       m_doc->tempDir() );
0273 
0274         m_tempData->prepareTempFileNames( m_doc->tempDir() );
0275 
0276         if( m_doc->mixedType() != K3b::MixedDoc::DATA_SECOND_SESSION ) {
0277             createIsoImage();
0278         }
0279         else {
0280             emit newTask( i18n("Creating audio image files") );
0281             m_currentAction = CREATING_AUDIO_IMAGE;
0282             m_audioImager->start();
0283         }
0284     }
0285 }
0286 
0287 
0288 void K3b::MixedJob::startSecondSession()
0289 {
0290     // start the next session
0291     m_currentAction = WRITING_ISO_IMAGE;
0292     if( d->copiesDone > 0 ) {
0293         // we only create the image once. This should not be a problem???
0294         if( !prepareWriter() || !startWriting() ) {
0295             cleanupAfterError();
0296             jobFinished(false);
0297         }
0298     }
0299     else if( m_doc->dummy() ) {
0300         // do not try to get ms info in simulation mode since the cd is empty!
0301         if( m_doc->onTheFly() ) {
0302             m_currentAction = PREPARING_DATA;
0303             m_isoImager->calculateSize();
0304         }
0305         else
0306             createIsoImage();
0307     }
0308     else {
0309         m_currentAction = FETCHING_MSMessageInfo;
0310         m_msInfoFetcher->setDevice( m_doc->burner() );
0311         m_msInfoFetcher->start();
0312     }
0313 }
0314 
0315 
0316 void K3b::MixedJob::slotMaxSpeedJobFinished( bool success )
0317 {
0318     d->maxSpeed = success;
0319     if( !success )
0320         emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), MessageWarning );
0321 
0322     if( m_doc->mixedType() != K3b::MixedDoc::DATA_SECOND_SESSION ) {
0323         m_currentAction = PREPARING_DATA;
0324         m_isoImager->calculateSize();
0325     }
0326     else {
0327         // we cannot calculate the size since we don't have the msinfo yet
0328         // so first write the audio session
0329         writeNextCopy();
0330     }
0331 }
0332 
0333 
0334 void K3b::MixedJob::writeNextCopy()
0335 {
0336     // the prepareWriter method needs the action to be set
0337     if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK )
0338         m_currentAction = WRITING_ISO_IMAGE;
0339     else
0340         m_currentAction = WRITING_AUDIO_IMAGE;
0341 
0342     if( !prepareWriter() || !startWriting() ) {
0343         cleanupAfterError();
0344         jobFinished(false);
0345     }
0346 }
0347 
0348 
0349 void K3b::MixedJob::cancel()
0350 {
0351     m_canceled = true;
0352 
0353     if( d->maxSpeedJob )
0354         d->maxSpeedJob->cancel();
0355 
0356     if( m_writer && m_writer->active() )
0357         m_writer->cancel();
0358     if ( m_isoImager->active() )
0359         m_isoImager->cancel();
0360     if ( m_audioImager->active() )
0361         m_audioImager->cancel();
0362     if ( m_msInfoFetcher->active() )
0363         m_msInfoFetcher->cancel();
0364 
0365 #ifdef __GNUC__
0366 #warning FIXME: wait for subjobs to finish after cancellation
0367 #endif
0368 
0369     emit infoMessage( i18n("Writing canceled."), K3b::Job::MessageError );
0370     removeBufferFiles();
0371     emit canceled();
0372     jobFinished(false);
0373 }
0374 
0375 
0376 void K3b::MixedJob::slotMsInfoFetched( bool success )
0377 {
0378     if( m_canceled || m_errorOccuredAndAlreadyReported )
0379         return;
0380 
0381     if( success ) {
0382         if( m_usedDataWritingApp == K3b::WritingAppCdrecord )
0383             m_isoImager->setMultiSessionInfo( m_msInfoFetcher->msInfo() );
0384         else  // cdrdao seems to write a 150 blocks pregap that is not used by cdrecord
0385             m_isoImager->setMultiSessionInfo( QString("%1,%2")
0386                                               .arg(m_msInfoFetcher->lastSessionStart())
0387                                               .arg(m_msInfoFetcher->nextSessionStart()+150) );
0388 
0389         if( m_doc->onTheFly() ) {
0390             m_currentAction = PREPARING_DATA;
0391             m_isoImager->calculateSize();
0392         }
0393         else {
0394             createIsoImage();
0395         }
0396     }
0397     else {
0398         // the MsInfoFetcher already emitted failure info
0399         cleanupAfterError();
0400         jobFinished(false);
0401     }
0402 }
0403 
0404 
0405 void K3b::MixedJob::slotIsoImagerFinished( bool success )
0406 {
0407     if( m_canceled || m_errorOccuredAndAlreadyReported )
0408         return;
0409 
0410     //
0411     // Initializing imager before the first copy
0412     //
0413     if( m_currentAction == INITIALIZING_IMAGER ) {
0414         if( success ) {
0415             m_currentAction = PREPARING_DATA;
0416 
0417             // check the size
0418             m_projectSize  = m_isoImager->size() + m_doc->audioDoc()->length();
0419             if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION )
0420                 m_projectSize += 11400; // the session gap
0421 
0422             startFirstCopy();
0423         }
0424         else {
0425             cleanupAfterError();
0426             jobFinished( false );
0427         }
0428     }
0429 
0430     //
0431     // Recalculated iso image size
0432     //
0433     else if( m_currentAction == PREPARING_DATA ) {
0434         if( success ) {
0435             // 1. data in first track:
0436             //    start isoimager and writer
0437             //    when isoimager finishes start audiodecoder
0438 
0439             // 2. data in last track
0440             //    start audiodecoder and writer
0441             //    when audiodecoder finishes start isoimager
0442 
0443             // 3. data in second session
0444             //    start audiodecoder and writer
0445             //    start isoimager and writer
0446 
0447             if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
0448                 m_currentAction = WRITING_ISO_IMAGE;
0449                 if( !prepareWriter() || !startWriting() ) {
0450                     cleanupAfterError();
0451                     jobFinished(false);
0452                 }
0453             }
0454             else
0455                 writeNextCopy();
0456         }
0457         else {
0458             cleanupAfterError();
0459             jobFinished( false );
0460         }
0461     }
0462 
0463     //
0464     // Image creation finished
0465     //
0466     else {
0467         if( !success ) {
0468             emit infoMessage( i18n("Error while creating ISO image."), MessageError );
0469             cleanupAfterError();
0470 
0471             jobFinished( false );
0472             return;
0473         }
0474 
0475         if( m_doc->onTheFly() ) {
0476             if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK ) {
0477                 m_currentAction = WRITING_AUDIO_IMAGE;
0478                 m_audioImager->writeTo( m_writer->ioDevice() );
0479                 m_audioImager->start();
0480             }
0481         }
0482         else {
0483             emit infoMessage( i18n("ISO image successfully created."), MessageSuccess );
0484 
0485             if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
0486                 m_currentAction = WRITING_ISO_IMAGE;
0487 
0488                 if( !prepareWriter() || !startWriting() ) {
0489                     cleanupAfterError();
0490                     jobFinished(false);
0491                 }
0492             }
0493             else {
0494                 emit newTask( i18n("Creating audio image files") );
0495                 m_currentAction = CREATING_AUDIO_IMAGE;
0496                 m_audioImager->start();
0497             }
0498         }
0499     }
0500 }
0501 
0502 
0503 void K3b::MixedJob::slotWriterFinished( bool success )
0504 {
0505     if( m_canceled || m_errorOccuredAndAlreadyReported )
0506         return;
0507 
0508     if( !success ) {
0509         cleanupAfterError();
0510         jobFinished(false);
0511         return;
0512     }
0513 
0514     emit burning(false);
0515 
0516     if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION && m_currentAction == WRITING_AUDIO_IMAGE ) {
0517         // many drives need to reload the medium to return to a proper state
0518         if ( ( int )m_doc->burner()->readToc().count() < m_doc->numOfTracks()-1 ) {
0519             emit infoMessage( i18n( "Need to reload medium to return to proper state." ), MessageInfo );
0520             connect( K3b::Device::reload( m_doc->burner() ),
0521                      SIGNAL(finished(K3b::Device::DeviceHandler*)),
0522                      this,
0523                      SLOT(slotMediaReloadedForSecondSession(K3b::Device::DeviceHandler*)) );
0524         }
0525         else {
0526             startSecondSession();
0527         }
0528     }
0529     else {
0530         d->copiesDone++;
0531         if( d->copiesDone < d->copies ) {
0532             if( !K3b::eject( m_doc->burner() ) ) {
0533                 blockingInformation( i18n("K3b was unable to eject the written disk. Please do so manually.") );
0534             }
0535             writeNextCopy();
0536         }
0537         else {
0538             if( !m_doc->onTheFly() && m_doc->removeImages() )
0539                 removeBufferFiles();
0540 
0541             if ( k3bcore->globalSettings()->ejectMedia() ) {
0542                 K3b::Device::eject( m_doc->burner() );
0543             }
0544 
0545             jobFinished(true);
0546         }
0547     }
0548 }
0549 
0550 
0551 void K3b::MixedJob::slotMediaReloadedForSecondSession( K3b::Device::DeviceHandler* dh )
0552 {
0553     if( !dh->success() ) {
0554         blockingInformation( i18n("Please reload the medium and press 'OK'"),
0555                              i18n("Unable to close the tray") );
0556     }
0557 
0558     startSecondSession();
0559 }
0560 
0561 
0562 void K3b::MixedJob::slotAudioDecoderFinished( bool success )
0563 {
0564     if( m_canceled || m_errorOccuredAndAlreadyReported )
0565         return;
0566 
0567     if( !success ) {
0568         emit infoMessage( i18n("Error while decoding audio tracks."), MessageError );
0569         cleanupAfterError();
0570         jobFinished(false);
0571         return;
0572     }
0573 
0574     if( m_doc->onTheFly() ) {
0575         if( m_doc->mixedType() == K3b::MixedDoc::DATA_LAST_TRACK ) {
0576             m_currentAction = WRITING_ISO_IMAGE;
0577             m_isoImager->start();
0578             d->pipe.readFrom( m_isoImager->ioDevice() );
0579             d->pipe.writeTo( m_writer->ioDevice() );
0580             d->pipe.open();
0581         }
0582     }
0583     else {
0584         emit infoMessage( i18n("Audio images successfully created."), MessageSuccess );
0585 
0586         if( m_doc->audioDoc()->normalize() ) {
0587             normalizeFiles();
0588         }
0589         else {
0590             if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK )
0591                 m_currentAction = WRITING_ISO_IMAGE;
0592             else
0593                 m_currentAction = WRITING_AUDIO_IMAGE;
0594 
0595             if( !prepareWriter() || !startWriting() ) {
0596                 cleanupAfterError();
0597                 jobFinished(false);
0598             }
0599         }
0600     }
0601 }
0602 
0603 
0604 void K3b::MixedJob::slotAudioDecoderNextTrack( int t, int tt )
0605 {
0606     if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
0607         K3b::AudioTrack* track = m_doc->audioDoc()->getTrack(t);
0608         emit newSubTask( i18n("Decoding audio track %1 of %2%3",
0609                               t,
0610                               tt,
0611                               ( track->title().isEmpty() || track->artist().isEmpty()
0612                                 ? QString()
0613                                 : " (" + track->artist() + " - " + track->title() + ')' ) ) );
0614     }
0615 }
0616 
0617 
0618 bool K3b::MixedJob::prepareWriter()
0619 {
0620     delete m_writer;
0621     m_writer = 0;
0622 
0623     if( ( m_currentAction == WRITING_ISO_IMAGE && m_usedDataWritingApp == K3b::WritingAppCdrecord ) ||
0624         ( m_currentAction == WRITING_AUDIO_IMAGE && m_usedAudioWritingApp == K3b::WritingAppCdrecord ) )  {
0625 
0626         if( !writeInfFiles() ) {
0627             qDebug() << "(K3b::MixedJob) could not write inf-files.";
0628             emit infoMessage( i18n("I/O Error"), MessageError );
0629 
0630             return false;
0631         }
0632 
0633         K3b::CdrecordWriter* writer = new K3b::CdrecordWriter( m_doc->burner(), this, this );
0634 
0635         // only write the audio tracks in DAO mode
0636         if( m_currentAction == WRITING_ISO_IMAGE )
0637             writer->setWritingMode( m_usedDataWritingMode );
0638         else
0639             writer->setWritingMode( m_usedAudioWritingMode );
0640 
0641         writer->setSimulate( m_doc->dummy() );
0642         writer->setBurnSpeed( m_doc->speed() );
0643 
0644         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
0645             if( m_currentAction == WRITING_ISO_IMAGE ) {
0646                 if( m_doc->onTheFly() )
0647                     writer->addArgument("-waiti");
0648 
0649                 addDataTrack( writer );
0650             }
0651             else {
0652                 writer->setMulti( true );
0653                 addAudioTracks( writer );
0654             }
0655         }
0656         else {
0657             if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK )
0658                 addDataTrack( writer );
0659             addAudioTracks( writer );
0660             if( m_doc->mixedType() == K3b::MixedDoc::DATA_LAST_TRACK )
0661                 addDataTrack( writer );
0662         }
0663 
0664         m_writer = writer;
0665     }
0666     else {
0667         if( !writeTocFile() ) {
0668             qDebug() << "(K3b::DataJob) could not write tocfile.";
0669             emit infoMessage( i18n("I/O Error"), MessageError );
0670 
0671             return false;
0672         }
0673 
0674         // create the writer
0675         // create cdrdao job
0676         K3b::CdrdaoWriter* writer = new K3b::CdrdaoWriter( m_doc->burner(), this, this );
0677         writer->setSimulate( m_doc->dummy() );
0678         writer->setBurnSpeed( m_doc->speed() );
0679 
0680         // multisession only for the first session
0681         writer->setMulti( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION
0682                           && m_currentAction == WRITING_AUDIO_IMAGE );
0683 
0684         writer->setTocFile( m_tocFile->fileName() );
0685 
0686         m_writer = writer;
0687     }
0688 
0689     connect( m_writer, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0690     connect( m_writer, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) );
0691     connect( m_writer, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSize(int,int)) );
0692     connect( m_writer, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
0693     connect( m_writer, SIGNAL(processedSubSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
0694     connect( m_writer, SIGNAL(nextTrack(int,int)), this, SLOT(slotWriterNextTrack(int,int)) );
0695     connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
0696     connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
0697     connect( m_writer, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
0698     connect( m_writer, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
0699     //  connect( m_writer, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) );
0700     connect( m_writer, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) );
0701     connect( m_writer, SIGNAL(debuggingOutput(QString,QString)),
0702              this, SIGNAL(debuggingOutput(QString,QString)) );
0703 
0704     return true;
0705 }
0706 
0707 
0708 bool K3b::MixedJob::writeInfFiles()
0709 {
0710     K3b::InfFileWriter infFileWriter;
0711     K3b::AudioTrack* track = m_doc->audioDoc()->firstTrack();
0712     while( track ) {
0713 
0714         infFileWriter.setTrack( track->toCdTrack() );
0715         infFileWriter.setTrackNumber( track->trackNumber() );
0716         if( !m_doc->onTheFly() )
0717             infFileWriter.setBigEndian( false );
0718 
0719         if( !infFileWriter.save( m_tempData->infFileName(track) ) )
0720             return false;
0721 
0722         track = track->next();
0723     }
0724     return true;
0725 }
0726 
0727 
0728 bool K3b::MixedJob::writeTocFile()
0729 {
0730     // FIXME: create the tocfile in the same directory like all the other files.
0731 
0732     delete m_tocFile;
0733     m_tocFile = new QTemporaryFile( "XXXXXX.toc" );
0734     m_tocFile->open();
0735 
0736     // write the toc-file
0737     QTextStream s( m_tocFile );
0738 
0739     K3b::TocFileWriter tocFileWriter;
0740 
0741     //
0742     // TOC
0743     //
0744     tocFileWriter.setData( m_doc->toToc( m_usedDataMode == K3b::DataMode2
0745                                          ? K3b::Device::Track::XA_FORM1
0746                                          : K3b::Device::Track::MODE1,
0747                                          m_doc->onTheFly()
0748                                          ? m_isoImager->size()
0749                                          : m_doc->dataDoc()->length() ) );
0750 
0751     //
0752     // CD-Text
0753     //
0754     if( m_doc->audioDoc()->cdText() ) {
0755         K3b::Device::CdText text = m_doc->audioDoc()->cdTextData();
0756         // if data in first track we need to add a dummy cdtext
0757         if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK )
0758             text.insert( 0, K3b::Device::TrackCdText() );
0759 
0760         tocFileWriter.setCdText( text );
0761     }
0762 
0763     //
0764     // Session to write
0765     //
0766     tocFileWriter.setSession( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION &&
0767                               m_currentAction == WRITING_ISO_IMAGE ? 2 : 1 );
0768 
0769     //
0770     // image filenames
0771     //
0772     if( !m_doc->onTheFly() ) {
0773         QStringList files;
0774         K3b::AudioTrack* track = m_doc->audioDoc()->firstTrack();
0775         while( track ) {
0776             files += m_tempData->bufferFileName( track );
0777             track = track->next();
0778         }
0779         if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK )
0780             files.prepend( m_isoImageFilePath );
0781         else
0782             files.append( m_isoImageFilePath );
0783 
0784         tocFileWriter.setFilenames( files );
0785     }
0786 
0787     bool success = tocFileWriter.save( s );
0788 
0789     m_tocFile->close();
0790 
0791     // backup for debugging
0792 //    KIO::del("/tmp/trueg/tocfile_debug_backup.toc",0L)->exec();
0793 //    KIO::copyAs( m_tocFile->name(), "/tmp/trueg/tocfile_debug_backup.toc",0L )->exec();
0794 
0795     return success;
0796 }
0797 
0798 
0799 void K3b::MixedJob::addAudioTracks( K3b::CdrecordWriter* writer )
0800 {
0801     writer->addArgument( "-useinfo" );
0802 
0803     // add raw cdtext data
0804     if( m_doc->audioDoc()->cdText() ) {
0805         writer->setRawCdText( m_doc->audioDoc()->cdTextData().rawPackData() );
0806     }
0807 
0808     writer->addArgument( "-audio" );
0809 
0810     // we always pad because although K3b makes sure all tracks' length are multiples of 2352
0811     // it seems that normalize sometimes corrupts these lengths
0812     // FIXME: see K3b::AudioJob for the whole less4secs and zeroPregap handling
0813     writer->addArgument( "-pad" );
0814 
0815     // Allow tracks shorter than 4 seconds
0816     writer->addArgument( "-shorttrack" );
0817 
0818     // add all the audio tracks
0819     K3b::AudioTrack* track = m_doc->audioDoc()->firstTrack();
0820     while( track ) {
0821         if( m_doc->onTheFly() ) {
0822             // this is only supported by cdrecord versions >= 2.01a13
0823             writer->addArgument( QFile::encodeName( m_tempData->infFileName( track ) ) );
0824         }
0825         else {
0826             writer->addArgument( QFile::encodeName( m_tempData->bufferFileName( track ) ) );
0827         }
0828         track = track->next();
0829     }
0830 }
0831 
0832 void K3b::MixedJob::addDataTrack( K3b::CdrecordWriter* writer )
0833 {
0834     // add data track
0835     if(  m_usedDataMode == K3b::DataMode2 ) {
0836         const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
0837         if( cdrecordBin && cdrecordBin->hasFeature( "xamix" ) )
0838             writer->addArgument( "-xa" );
0839         else
0840             writer->addArgument( "-xa1" );
0841     }
0842     else
0843         writer->addArgument( "-data" );
0844 
0845     if( m_doc->onTheFly() )
0846         writer->addArgument( QString("-tsize=%1s").arg(m_isoImager->size()) )->addArgument("-");
0847     else
0848         writer->addArgument( m_isoImageFilePath );
0849 }
0850 
0851 
0852 void K3b::MixedJob::slotWriterNextTrack( int t, int )
0853 {
0854     K3b::AudioTrack* track = 0;
0855 
0856     if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK ) {
0857         if( t > 1 )
0858             track = m_doc->audioDoc()->getTrack(t-1);
0859     }
0860     else if( m_doc->mixedType() == K3b::MixedDoc::DATA_LAST_TRACK ) {
0861         if( t < m_doc->audioDoc()->numOfTracks()+1 )
0862             track = m_doc->audioDoc()->getTrack(t);
0863     }
0864     else if( m_currentAction == WRITING_AUDIO_IMAGE )
0865         track = m_doc->audioDoc()->getTrack(t);
0866     else
0867         t = m_doc->numOfTracks();
0868 
0869     if( track )
0870         emit newSubTask( i18n("Writing track %1 of %2%3"
0871                               ,t
0872                               ,m_doc->numOfTracks()
0873                               , track->title().isEmpty() || track->artist().isEmpty()
0874                               ? QString()
0875                               : " (" + track->artist() + " - " + track->title() + ')' ) );
0876     else
0877         emit newSubTask( i18n("Writing track %1 of %2 (%3)",
0878                               t,
0879                               m_doc->numOfTracks(),
0880                               i18n("ISO 9660 data")) );
0881 }
0882 
0883 
0884 void K3b::MixedJob::slotWriterJobPercent( int p )
0885 {
0886     double totalTasks = d->copies;
0887     double tasksDone = d->copiesDone;
0888     if( m_doc->audioDoc()->normalize() ) {
0889         totalTasks+=1.0;
0890         tasksDone+=1.0;
0891     }
0892     if( !m_doc->onTheFly() ) {
0893         totalTasks+=1.0;
0894     }
0895 
0896     if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
0897         if( m_currentAction == WRITING_AUDIO_IMAGE ) {
0898             // the audio imager has finished in all cases
0899             // the iso imager only if this is not the first copy
0900             if( d->copiesDone > 0 )
0901                 tasksDone += 1.0;
0902             else if( !m_doc->onTheFly() )
0903                 tasksDone += m_audioDocPartOfProcess;
0904 
0905             p = (int)((double)p*m_audioDocPartOfProcess);
0906         }
0907         else {
0908             // all images have been created
0909             if( !m_doc->onTheFly() )
0910                 tasksDone += 1.0;
0911 
0912             p = (int)(100.0*m_audioDocPartOfProcess + (double)p*(1.0-m_audioDocPartOfProcess));
0913         }
0914     }
0915     else if( !m_doc->onTheFly() )
0916         tasksDone += 1.0;
0917 
0918     emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0919 }
0920 
0921 
0922 void K3b::MixedJob::slotAudioDecoderPercent( int p )
0923 {
0924     // the only thing finished here might be the isoimager which is part of this task
0925     if( !m_doc->onTheFly() ) {
0926         double totalTasks = d->copies+1;
0927         if( m_doc->audioDoc()->normalize() )
0928             totalTasks+=1.0;
0929 
0930         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION )
0931             p = (int)((double)p*m_audioDocPartOfProcess);
0932         else
0933             p = (int)(100.0*(1.0-m_audioDocPartOfProcess) + (double)p*m_audioDocPartOfProcess);
0934 
0935         emit percent( (int)((double)p / totalTasks) );
0936     }
0937 }
0938 
0939 
0940 void K3b::MixedJob::slotAudioDecoderSubPercent( int p )
0941 {
0942     if( !m_doc->onTheFly() ) {
0943         emit subPercent( p );
0944     }
0945 }
0946 
0947 
0948 void K3b::MixedJob::slotIsoImagerPercent( int p )
0949 {
0950     if( !m_doc->onTheFly() ) {
0951         emit subPercent( p );
0952         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
0953 
0954             double totalTasks = d->copies+1.0;
0955             double tasksDone = d->copiesDone;
0956             if( m_doc->audioDoc()->normalize() ) {
0957                 totalTasks+=1.0;
0958                 // the normalizer finished
0959                 tasksDone+=1.0;
0960             }
0961 
0962             // the writing of the audio part finished
0963             tasksDone += m_audioDocPartOfProcess;
0964 
0965             // the audio decoder finished (which is part of this task in terms of progress)
0966             p = (int)(100.0*m_audioDocPartOfProcess + (double)p*(1.0-m_audioDocPartOfProcess));
0967 
0968             emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
0969         }
0970         else {
0971             double totalTasks = d->copies+1.0;
0972             if( m_doc->audioDoc()->normalize() )
0973                 totalTasks+=1.0;
0974 
0975             emit percent( (int)((double)(p*(1.0-m_audioDocPartOfProcess)) / totalTasks) );
0976         }
0977     }
0978 }
0979 
0980 
0981 bool K3b::MixedJob::startWriting()
0982 {
0983     if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
0984         if( m_currentAction == WRITING_ISO_IMAGE) {
0985             if( m_doc->dummy() )
0986                 emit newTask( i18n("Simulating second session") );
0987             else if( d->copies > 1 )
0988                 emit newTask( i18n("Writing second session of copy %1", d->copiesDone+1) );
0989             else
0990                 emit newTask( i18n("Writing second session") );
0991         }
0992         else {
0993             if( m_doc->dummy() )
0994                 emit newTask( i18n("Simulating first session") );
0995             else if( d->copies > 1 )
0996                 emit newTask( i18n("Writing first session of copy %1", d->copiesDone+1) );
0997             else
0998                 emit newTask( i18n("Writing first session") );
0999         }
1000     }
1001     else if( m_doc->dummy() )
1002         emit newTask( i18n("Simulating") );
1003     else
1004         emit newTask( i18n("Writing Copy %1", d->copiesDone+1) );
1005 
1006 
1007     // if we append the second session the cd is already in the drive
1008     if( !(m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION
1009           && m_currentAction == WRITING_ISO_IMAGE) ) {
1010 
1011         emit newSubTask( i18n("Waiting for media") );
1012         if( waitForMedium( m_doc->burner() ) == Device::MEDIA_UNKNOWN ) {
1013             cancel();
1014             return false;
1015         }
1016 
1017         // just to be sure we did not get canceled during the async discWaiting
1018         if( m_canceled )
1019             return false;
1020 
1021         // check if the project will fit on the CD
1022         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
1023             // the media is in and has been checked so this should be fast (hopefully)
1024             K3b::Msf mediaSize = m_doc->burner()->diskInfo().capacity();
1025             if( mediaSize < m_projectSize ) {
1026                 if( k3bcore->globalSettings()->overburn() ) {
1027                     emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3b::Job::MessageWarning );
1028                 }
1029                 else {
1030                     emit infoMessage( i18n("Data does not fit on disk."), MessageError );
1031                     return false;
1032                 }
1033             }
1034         }
1035     }
1036 
1037     // in case we determined the max possible writing speed we have to reset the speed on the writer job
1038     // here since an inserted media is necessary
1039     // the Max speed job will compare the max speed value with the supported values of the writer
1040     if( d->maxSpeed )
1041         m_writer->setBurnSpeed( d->maxSpeedJob->maxSpeed() );
1042 
1043     emit burning(true);
1044     m_writer->start();
1045 
1046     if( m_doc->onTheFly() ) {
1047         if ( m_currentAction == WRITING_AUDIO_IMAGE ) {
1048             // now the writer is running and we can get it's stdin
1049             // we only use this method when writing on-the-fly since
1050             // we cannot easily change the audioDecode fd while it's working
1051             // which we would need to do since we write into several
1052             // image files.
1053             m_audioImager->writeTo( m_writer->ioDevice() );
1054             m_audioImager->start();
1055         }
1056         else {
1057             m_isoImager->start();
1058             d->pipe.readFrom( m_isoImager->ioDevice() );
1059             d->pipe.writeTo( m_writer->ioDevice() );
1060             d->pipe.open();
1061         }
1062     }
1063 
1064     return true;
1065 }
1066 
1067 
1068 void K3b::MixedJob::createIsoImage()
1069 {
1070     m_currentAction = CREATING_ISO_IMAGE;
1071 
1072     // prepare iso image file
1073     m_isoImageFilePath = m_tempFilePrefix + "_datatrack.iso";
1074 
1075     if( !m_doc->onTheFly() )
1076         emit newTask( i18n("Creating ISO image file") );
1077     emit newSubTask( i18n("Creating ISO image in %1", m_isoImageFilePath) );
1078     emit infoMessage( i18n("Creating ISO image in %1", m_isoImageFilePath), MessageInfo );
1079 
1080     d->dataImageFile.setName( m_isoImageFilePath );
1081     if ( d->dataImageFile.open( QIODevice::WriteOnly ) ) {
1082         m_isoImager->start();
1083         d->pipe.readFrom( m_isoImager->ioDevice() );
1084         d->pipe.writeTo( &d->dataImageFile, true );
1085         d->pipe.open( true );
1086     }
1087     else {
1088         emit infoMessage( i18n("Could not open %1 for writing", m_isoImageFilePath ), MessageError );
1089         cleanupAfterError();
1090         jobFinished(false);
1091     }
1092 }
1093 
1094 
1095 void K3b::MixedJob::cleanupAfterError()
1096 {
1097     m_errorOccuredAndAlreadyReported = true;
1098     //  m_audioImager->cancel();
1099     m_isoImager->cancel();
1100     if( m_writer && m_writer->active() )
1101         m_writer->cancel();
1102 
1103     delete m_tocFile;
1104     m_tocFile = 0;
1105 
1106     // remove the temp files
1107     removeBufferFiles();
1108 
1109 #ifdef __GNUC__
1110 #warning FIXME: eject medium if necessary after cleanupAfterError
1111 #endif
1112 }
1113 
1114 
1115 void K3b::MixedJob::removeBufferFiles()
1116 {
1117     if ( !m_doc->onTheFly() ) {
1118         emit infoMessage( i18n("Removing buffer files."), MessageInfo );
1119     }
1120 
1121     if( QFile::exists( m_isoImageFilePath ) )
1122         if( !QFile::remove( m_isoImageFilePath ) )
1123             emit infoMessage( i18n("Could not delete file %1.",m_isoImageFilePath), MessageError );
1124 
1125     // removes buffer images and temp toc or inf files
1126     m_tempData->cleanup();
1127 }
1128 
1129 
1130 void K3b::MixedJob::determineWritingMode()
1131 {
1132     // we don't need this when only creating image and it is possible
1133     // that the burn device is null
1134     if( m_doc->onlyCreateImages() )
1135         return;
1136 
1137     // at first we determine the data mode
1138     // --------------------------------------------------------------
1139     if( m_doc->dataDoc()->dataMode() == K3b::DataModeAuto ) {
1140         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION )
1141             m_usedDataMode = K3b::DataMode2;
1142         else
1143             m_usedDataMode = K3b::DataMode1;
1144     }
1145     else
1146         m_usedDataMode = m_doc->dataDoc()->dataMode();
1147 
1148     const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
1149     // we try to use cdrecord if possible
1150     bool cdrecordOnTheFly = false;
1151     bool cdrecordCdText = false;
1152     bool cdrecordUsable = false;
1153 
1154     if( cdrecordBin ) {
1155         cdrecordOnTheFly = cdrecordBin->hasFeature( "audio-stdin" );
1156         cdrecordCdText = cdrecordBin->hasFeature( "cdtext" );
1157         cdrecordUsable =
1158             !( !cdrecordOnTheFly && m_doc->onTheFly() ) &&
1159             !( m_doc->audioDoc()->cdText() && !cdrecordCdText );
1160     }
1161 
1162     // Writing Application
1163     // --------------------------------------------------------------
1164     // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system!
1165     if( writingApp() == K3b::WritingAppAuto ) {
1166         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
1167             if( m_doc->writingMode() == K3b::WritingModeSao ||
1168                 ( m_doc->writingMode() == K3b::WritingModeAuto && !cdrecordUsable ) ) {
1169                 m_usedAudioWritingApp = K3b::WritingAppCdrdao;
1170                 m_usedDataWritingApp = K3b::WritingAppCdrdao;
1171             }
1172             else {
1173                 m_usedAudioWritingApp = K3b::WritingAppCdrecord;
1174                 m_usedDataWritingApp = K3b::WritingAppCdrecord;
1175             }
1176         }
1177         else {
1178             if( cdrecordUsable ) {
1179                 m_usedAudioWritingApp = K3b::WritingAppCdrecord;
1180                 m_usedDataWritingApp = K3b::WritingAppCdrecord;
1181             }
1182             else {
1183                 m_usedAudioWritingApp = K3b::WritingAppCdrdao;
1184                 m_usedDataWritingApp = K3b::WritingAppCdrdao;
1185             }
1186         }
1187     }
1188     else {
1189         m_usedAudioWritingApp = writingApp();
1190         m_usedDataWritingApp = writingApp();
1191     }
1192 
1193     // TODO: use K3b::Exceptions::brokenDaoAudio
1194 
1195     // Writing Mode (TAO/DAO/RAW)
1196     // --------------------------------------------------------------
1197     if( m_doc->writingMode() == K3b::WritingModeAuto ) {
1198 
1199         if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
1200             if( m_usedDataWritingApp == K3b::WritingAppCdrecord )
1201                 m_usedDataWritingMode = K3b::WritingModeTao;
1202             else
1203                 m_usedDataWritingMode = K3b::WritingModeSao;
1204 
1205             // default to Session at once for the audio part
1206             m_usedAudioWritingMode = K3b::WritingModeSao;
1207         }
1208         else {
1209             m_usedDataWritingMode = K3b::WritingModeTao;
1210             m_usedAudioWritingMode = K3b::WritingModeTao;
1211         }
1212     }
1213     else {
1214         m_usedAudioWritingMode = m_doc->writingMode();
1215         m_usedDataWritingMode = m_doc->writingMode();
1216     }
1217 
1218 
1219     if( m_usedDataWritingApp == K3b::WritingAppCdrecord ) {
1220         if( !cdrecordOnTheFly && m_doc->onTheFly() ) {
1221             m_doc->setOnTheFly( false );
1222             emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), MessageError );
1223         }
1224 
1225         if( m_doc->audioDoc()->cdText() ) {
1226             if( !cdrecordCdText ) {
1227                 m_doc->audioDoc()->writeCdText( false );
1228                 if (cdrecordBin) {
1229                     emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.",cdrecordBin->version()), MessageError );
1230                 } else {
1231                     emit infoMessage( i18n("Cdrecord could not be found on your system." ), MessageError );
1232                 }
1233             }
1234             else if( m_usedAudioWritingMode == K3b::WritingModeTao ) {
1235                 emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode. Try DAO or RAW."), MessageWarning );
1236             }
1237         }
1238     }
1239 }
1240 
1241 
1242 void K3b::MixedJob::normalizeFiles()
1243 {
1244     if( !m_normalizeJob ) {
1245         m_normalizeJob = new K3b::AudioNormalizeJob( this, this );
1246 
1247         connect( m_normalizeJob, SIGNAL(infoMessage(QString,int)),
1248                  this, SIGNAL(infoMessage(QString,int)) );
1249         connect( m_normalizeJob, SIGNAL(percent(int)), this, SLOT(slotNormalizeProgress(int)) );
1250         connect( m_normalizeJob, SIGNAL(subPercent(int)), this, SLOT(slotNormalizeSubProgress(int)) );
1251         connect( m_normalizeJob, SIGNAL(finished(bool)), this, SLOT(slotNormalizeJobFinished(bool)) );
1252         connect( m_normalizeJob, SIGNAL(newTask(QString)), this, SIGNAL(newSubTask(QString)) );
1253         connect( m_normalizeJob, SIGNAL(debuggingOutput(QString,QString)),
1254                  this, SIGNAL(debuggingOutput(QString,QString)) );
1255     }
1256 
1257     // add all the files
1258     QList<QString> files;
1259     K3b::AudioTrack* track = m_doc->audioDoc()->firstTrack();
1260     while( track ) {
1261         files.append( m_tempData->bufferFileName(track) );
1262         track = track->next();
1263     }
1264 
1265     m_normalizeJob->setFilesToNormalize( files );
1266 
1267     emit newTask( i18n("Normalizing volume levels") );
1268     m_normalizeJob->start();
1269 }
1270 
1271 void K3b::MixedJob::slotNormalizeJobFinished( bool success )
1272 {
1273     if( m_canceled || m_errorOccuredAndAlreadyReported )
1274         return;
1275 
1276     if( success ) {
1277         if( m_doc->mixedType() == K3b::MixedDoc::DATA_FIRST_TRACK )
1278             m_currentAction = WRITING_ISO_IMAGE;
1279         else
1280             m_currentAction = WRITING_AUDIO_IMAGE;
1281 
1282         if( !prepareWriter() || !startWriting() ) {
1283             cleanupAfterError();
1284             jobFinished(false);
1285         }
1286     }
1287     else {
1288         cleanupAfterError();
1289         jobFinished(false);
1290     }
1291 }
1292 
1293 void K3b::MixedJob::slotNormalizeProgress( int p )
1294 {
1295     double totalTasks = d->copies+2.0;
1296     double tasksDone = 0;
1297 
1298     if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION ) {
1299         // the audio imager finished (m_audioDocPartOfProcess*1 task)
1300         // plus the normalize progress
1301         tasksDone = m_audioDocPartOfProcess;
1302     }
1303     else {
1304         // the iso and audio imagers already finished (one task)
1305         // plus the normalize progress
1306         tasksDone = 1.0;
1307     }
1308 
1309     emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
1310 }
1311 
1312 
1313 void K3b::MixedJob::slotNormalizeSubProgress( int p )
1314 {
1315     emit subPercent( p );
1316 }
1317 
1318 
1319 void K3b::MixedJob::prepareProgressInformation()
1320 {
1321     // calculate percentage of audio and data
1322     // this is also used in on-the-fly mode
1323     double ds = (double)m_doc->dataDoc()->length().totalFrames();
1324     double as = (double)m_doc->audioDoc()->length().totalFrames();
1325     m_audioDocPartOfProcess = as/(ds+as);
1326 }
1327 
1328 
1329 QString K3b::MixedJob::jobDescription() const
1330 {
1331     if( m_doc->mixedType() == K3b::MixedDoc::DATA_SECOND_SESSION )
1332         return i18n("Writing Enhanced Audio CD")
1333             + ( m_doc->audioDoc()->title().isEmpty()
1334                 ? QString()
1335                 : QString( " (%1)" ).arg(m_doc->audioDoc()->title()) );
1336     else
1337         return i18n("Writing Mixed Mode CD")
1338             + ( m_doc->audioDoc()->title().isEmpty()
1339                 ? QString()
1340                 : QString( " (%1)" ).arg(m_doc->audioDoc()->title()) );
1341 }
1342 
1343 
1344 QString K3b::MixedJob::jobDetails() const
1345 {
1346     return ( i18ncp("%2 is of form XX:YY:ZZ, no pluralization needed"
1347                   ,"1 track (%2 minutes audio data, %3 ISO 9660 data)"
1348                   ,"%1 tracks (%2 minutes audio data, %3 ISO 9660 data)"
1349                   ,m_doc->numOfTracks()
1350                   ,m_doc->audioDoc()->length().toString()
1351                   ,KIO::convertSize(m_doc->dataDoc()->size()))
1352              + ( m_doc->copies() > 1 && !m_doc->dummy()
1353                  ? i18np(" - %1 copy", " - %1 copies", m_doc->copies())
1354                  : QString() ) );
1355 }
1356 
1357 #include "moc_k3bmixedjob.cpp"