diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-03 02:15:56 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-03 02:15:56 +0000 |
commit | 50b48aec6ddd451a6d1709c0942477b503457663 (patch) | |
tree | a9ece53ec06fd0a2819de7a2a6de997193566626 /libk3b/projects/mixedcd/k3bmixedjob.cpp | |
download | k3b-50b48aec6ddd451a6d1709c0942477b503457663.tar.gz k3b-50b48aec6ddd451a6d1709c0942477b503457663.zip |
Added abandoned KDE3 version of K3B
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k3b@1084400 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libk3b/projects/mixedcd/k3bmixedjob.cpp')
-rw-r--r-- | libk3b/projects/mixedcd/k3bmixedjob.cpp | 1339 |
1 files changed, 1339 insertions, 0 deletions
diff --git a/libk3b/projects/mixedcd/k3bmixedjob.cpp b/libk3b/projects/mixedcd/k3bmixedjob.cpp new file mode 100644 index 0000000..a4be92c --- /dev/null +++ b/libk3b/projects/mixedcd/k3bmixedjob.cpp @@ -0,0 +1,1339 @@ +/* + * + * $Id: k3bmixedjob.cpp 690212 2007-07-20 11:02:13Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <[email protected]> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]> + * + * 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. + * See the file "COPYING" for the exact licensing terms. + */ + + + + +#include "k3bmixedjob.h" +#include "k3bmixeddoc.h" + +#include <k3bdatadoc.h> +#include <k3bisoimager.h> +#include <k3bmsinfofetcher.h> +#include <k3baudioimager.h> +#include <k3baudiodoc.h> +#include <k3baudiotrack.h> +#include <k3baudionormalizejob.h> +#include <k3baudiojobtempdata.h> +#include <k3baudiomaxspeedjob.h> +#include <k3bdevicemanager.h> +#include <k3bdevice.h> +#include <k3bdevicehandler.h> +#include <k3bmsf.h> +#include <k3bglobals.h> +#include <k3bexternalbinmanager.h> +#include <k3bversion.h> +#include <k3bcore.h> +#include <k3bcdrecordwriter.h> +#include <k3bcdrdaowriter.h> +#include <k3btocfilewriter.h> +#include <k3binffilewriter.h> +#include <k3bglobalsettings.h> +#include <k3baudiofile.h> + +#include <qfile.h> +#include <qdatastream.h> +#include <qapplication.h> + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kio/netaccess.h> +#include <kio/global.h> +#include <kstringhandler.h> + + +static QString createNonExistingFilesString( const QValueList<K3bAudioFile*>& items, unsigned int max ) +{ + QString s; + unsigned int cnt = 0; + for( QValueList<K3bAudioFile*>::const_iterator it = items.begin(); + it != items.end(); ++it ) { + + s += KStringHandler::csqueeze( (*it)->filename(), 60 ); + + ++cnt; + if( cnt >= max || it == items.end() ) + break; + + s += "<br>"; + } + + if( items.count() > max ) + s += "..."; + + return s; +} + + + +class K3bMixedJob::Private +{ +public: + Private() + : maxSpeedJob(0) { + } + + + int copies; + int copiesDone; + + K3bAudioMaxSpeedJob* maxSpeedJob; + bool maxSpeed; +}; + + +K3bMixedJob::K3bMixedJob( K3bMixedDoc* doc, K3bJobHandler* hdl, QObject* parent ) + : K3bBurnJob( hdl, parent ), + m_doc( doc ), + m_normalizeJob(0) +{ + d = new Private; + + m_isoImager = new K3bIsoImager( doc->dataDoc(), this, this ); + connect( m_isoImager, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_isoImager, SIGNAL(percent(int)), this, SLOT(slotIsoImagerPercent(int)) ); + connect( m_isoImager, SIGNAL(finished(bool)), this, SLOT(slotIsoImagerFinished(bool)) ); + connect( m_isoImager, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); + + m_audioImager = new K3bAudioImager( doc->audioDoc(), this, this ); + connect( m_audioImager, SIGNAL(infoMessage(const QString&, int)), + this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_audioImager, SIGNAL(percent(int)), this, SLOT(slotAudioDecoderPercent(int)) ); + connect( m_audioImager, SIGNAL(subPercent(int)), this, SLOT(slotAudioDecoderSubPercent(int)) ); + connect( m_audioImager, SIGNAL(finished(bool)), this, SLOT(slotAudioDecoderFinished(bool)) ); + connect( m_audioImager, SIGNAL(nextTrack(int, int)), this, SLOT(slotAudioDecoderNextTrack(int, int)) ); + + m_msInfoFetcher = new K3bMsInfoFetcher( this, this ); + connect( m_msInfoFetcher, SIGNAL(finished(bool)), this, SLOT(slotMsInfoFetched(bool)) ); + connect( m_msInfoFetcher, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) ); + + m_writer = 0; + m_tocFile = 0; + m_tempData = new K3bAudioJobTempData( m_doc->audioDoc(), this ); +} + + +K3bMixedJob::~K3bMixedJob() +{ + delete m_tocFile; + delete d; +} + + +K3bDevice::Device* K3bMixedJob::writer() const +{ + if( m_doc->onlyCreateImages() ) + return 0; + else + return m_doc->burner(); +} + + +K3bDoc* K3bMixedJob::doc() const +{ + return m_doc; +} + + +void K3bMixedJob::start() +{ + jobStarted(); + + m_canceled = false; + m_errorOccuredAndAlreadyReported = false; + d->copiesDone = 0; + d->copies = m_doc->copies(); + m_currentAction = PREPARING_DATA; + d->maxSpeed = false; + + if( m_doc->dummy() ) + d->copies = 1; + + prepareProgressInformation(); + + // + // Check if all files exist + // + QValueList<K3bAudioFile*> nonExistingFiles; + K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); + while( track ) { + K3bAudioDataSource* source = track->firstSource(); + while( source ) { + if( K3bAudioFile* file = dynamic_cast<K3bAudioFile*>( source ) ) { + if( !QFile::exists( file->filename() ) ) + nonExistingFiles.append( file ); + } + source = source->next(); + } + track = track->next(); + } + if( !nonExistingFiles.isEmpty() ) { + if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the " + "project and continue without adding them to the image?") + + "<p>" + createNonExistingFilesString( nonExistingFiles, 10 ), + i18n("Warning"), + i18n("Remove missing files and continue"), + i18n("Cancel and go back") ) ) { + for( QValueList<K3bAudioFile*>::const_iterator it = nonExistingFiles.begin(); + it != nonExistingFiles.end(); ++it ) { + delete *it; + } + } + else { + m_canceled = true; + emit canceled(); + jobFinished(false); + return; + } + } + + // + // Make sure the project is not empty + // + if( m_doc->audioDoc()->numOfTracks() == 0 ) { + emit infoMessage( i18n("Please add files to your project first."), ERROR ); + jobFinished(false); + return; + } + + + // set some flags that are needed + m_doc->audioDoc()->setOnTheFly( m_doc->onTheFly() ); // for the toc writer + m_doc->audioDoc()->setHideFirstTrack( false ); // unsupported + m_doc->dataDoc()->setBurner( m_doc->burner() ); // so the isoImager can read ms data + + emit newTask( i18n("Preparing data") ); + + determineWritingMode(); + + // + // First we make sure the data portion is valid + // + + // we do not have msinfo yet + m_currentAction = INITIALIZING_IMAGER; + m_isoImager->setMultiSessionInfo( QString::null ); + m_isoImager->init(); +} + + +void K3bMixedJob::startFirstCopy() +{ + // + // if not onthefly create the iso image and then the wavs + // and write then + // if onthefly calculate the iso size + // + if( m_doc->onTheFly() ) { + if( m_doc->speed() == 0 ) { + emit newSubTask( i18n("Determining maximum writing speed") ); + + // + // try to determine the max possible speed + // no need to check the data track's max speed. Most current systems are able + // to handle the maxium possible + // + if( !d->maxSpeedJob ) { + // the maxspeed job gets the device from the doc: + m_doc->audioDoc()->setBurner( m_doc->burner() ); + d->maxSpeedJob = new K3bAudioMaxSpeedJob( m_doc->audioDoc(), this, this ); + connect( d->maxSpeedJob, SIGNAL(percent(int)), + this, SIGNAL(subPercent(int)) ); + connect( d->maxSpeedJob, SIGNAL(finished(bool)), + this, SLOT(slotMaxSpeedJobFinished(bool)) ); + } + d->maxSpeedJob->start(); + } + else if( m_doc->mixedType() != K3bMixedDoc::DATA_SECOND_SESSION ) { + m_currentAction = PREPARING_DATA; + m_isoImager->calculateSize(); + } + else { + // we cannot calculate the size since we don't have the msinfo yet + // so first write the audio session + writeNextCopy(); + } + } + else { + emit burning(false); + + emit infoMessage( i18n("Creating audio image files in %1").arg(m_doc->tempDir()), INFO ); + + m_tempFilePrefix = K3b::findUniqueFilePrefix( ( !m_doc->audioDoc()->title().isEmpty() + ? m_doc->audioDoc()->title() + : m_doc->dataDoc()->isoOptions().volumeID() ), + m_doc->tempDir() ); + + m_tempData->prepareTempFileNames( m_doc->tempDir() ); + QStringList filenames; + for( K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); track; track = track->next() ) + filenames += m_tempData->bufferFileName( track ); + m_audioImager->setImageFilenames( filenames ); + + if( m_doc->mixedType() != K3bMixedDoc::DATA_SECOND_SESSION ) { + createIsoImage(); + } + else { + emit newTask( i18n("Creating audio image files") ); + m_currentAction = CREATING_AUDIO_IMAGE; + m_audioImager->start(); + } + } +} + + +void K3bMixedJob::slotMaxSpeedJobFinished( bool success ) +{ + d->maxSpeed = success; + if( !success ) + emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), WARNING ); + + if( m_doc->mixedType() != K3bMixedDoc::DATA_SECOND_SESSION ) { + m_currentAction = PREPARING_DATA; + m_isoImager->calculateSize(); + } + else { + // we cannot calculate the size since we don't have the msinfo yet + // so first write the audio session + writeNextCopy(); + } +} + + +void K3bMixedJob::writeNextCopy() +{ + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + m_currentAction = WRITING_AUDIO_IMAGE; + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + else if( m_doc->onTheFly() ) + m_audioImager->start(); + } + else { + // the prepareWriter method needs the action to be set + if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) + m_currentAction = WRITING_AUDIO_IMAGE; + else + m_currentAction = WRITING_ISO_IMAGE; + + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + else if( m_doc->onTheFly() ) { + if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) + m_audioImager->start(); + else + m_isoImager->start(); + } + } +} + + +void K3bMixedJob::cancel() +{ + m_canceled = true; + + if( d->maxSpeedJob ) + d->maxSpeedJob->cancel(); + + if( m_writer ) + m_writer->cancel(); + m_isoImager->cancel(); + m_audioImager->cancel(); + m_msInfoFetcher->cancel(); + emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR ); + removeBufferFiles(); + emit canceled(); + jobFinished(false); +} + + +void K3bMixedJob::slotMsInfoFetched( bool success ) +{ + if( m_canceled || m_errorOccuredAndAlreadyReported ) + return; + + if( success ) { + if( m_usedDataWritingApp == K3b::CDRECORD ) + m_isoImager->setMultiSessionInfo( m_msInfoFetcher->msInfo() ); + else // cdrdao seems to write a 150 blocks pregap that is not used by cdrecord + m_isoImager->setMultiSessionInfo( QString("%1,%2") + .arg(m_msInfoFetcher->lastSessionStart()) + .arg(m_msInfoFetcher->nextSessionStart()+150) ); + + if( m_doc->onTheFly() ) { + m_currentAction = PREPARING_DATA; + m_isoImager->calculateSize(); + } + else { + createIsoImage(); + } + } + else { + // the MsInfoFetcher already emitted failure info + cleanupAfterError(); + jobFinished(false); + } +} + + +void K3bMixedJob::slotIsoImagerFinished( bool success ) +{ + if( m_canceled || m_errorOccuredAndAlreadyReported ) + return; + + // + // Initializing imager before the first copy + // + if( m_currentAction == INITIALIZING_IMAGER ) { + if( success ) { + m_currentAction = PREPARING_DATA; + + // check the size + m_projectSize = m_isoImager->size() + m_doc->audioDoc()->length(); + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) + m_projectSize += 11400; // the session gap + + startFirstCopy(); + } + else { + cleanupAfterError(); + jobFinished( false ); + } + } + + // + // Recalculated iso image size + // + else if( m_currentAction == PREPARING_DATA ) { + if( success ) { + // 1. data in first track: + // start isoimager and writer + // when isoimager finishes start audiodecoder + + // 2. data in last track + // start audiodecoder and writer + // when audiodecoder finishes start isoimager + + // 3. data in second session + // start audiodecoder and writer + // start isoimager and writer + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + m_currentAction = WRITING_ISO_IMAGE; + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + else + m_isoImager->start(); + } + else + writeNextCopy(); + } + else { + cleanupAfterError(); + jobFinished( false ); + } + } + + // + // Image creation finished + // + else { + if( !success ) { + emit infoMessage( i18n("Error while creating ISO image."), ERROR ); + cleanupAfterError(); + + jobFinished( false ); + return; + } + + if( m_doc->onTheFly() ) { + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) { + m_currentAction = WRITING_AUDIO_IMAGE; + m_audioImager->start(); + } + } + else { + emit infoMessage( i18n("ISO image successfully created."), SUCCESS ); + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + m_currentAction = WRITING_ISO_IMAGE; + + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + } + else { + emit newTask( i18n("Creating audio image files") ); + m_currentAction = CREATING_AUDIO_IMAGE; + m_audioImager->start(); + } + } + } +} + + +void K3bMixedJob::slotWriterFinished( bool success ) +{ + if( m_canceled || m_errorOccuredAndAlreadyReported ) + return; + + if( !success ) { + cleanupAfterError(); + jobFinished(false); + return; + } + + emit burning(false); + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION && m_currentAction == WRITING_AUDIO_IMAGE ) { + // reload the media (as a subtask so the user does not see the "Flushing cache" or "Fixating" messages while + // doing so + emit newSubTask( i18n("Reloading the medium") ); + connect( K3bDevice::reload( m_doc->burner() ), SIGNAL(finished(bool)), + this, SLOT(slotMediaReloadedForSecondSession(bool)) ); + } + else { + d->copiesDone++; + if( d->copiesDone < d->copies ) { + K3bDevice::eject( m_doc->burner() ); + writeNextCopy(); + } + else { + if( !m_doc->onTheFly() && m_doc->removeImages() ) + removeBufferFiles(); + + if( k3bcore->globalSettings()->ejectMedia() ) + K3bDevice::eject( m_doc->burner() ); + + jobFinished(true); + } + } +} + + +void K3bMixedJob::slotMediaReloadedForSecondSession( bool success ) +{ + if( !success ) + blockingInformation( i18n("Please reload the medium and press 'ok'"), + i18n("Unable to close the tray") ); + + // start the next session + m_currentAction = WRITING_ISO_IMAGE; + if( d->copiesDone > 0 ) { + // we only create the image once. This should not be a problem??? + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + else if( m_doc->onTheFly() ) { + m_isoImager->start(); + } + } + else if( m_doc->dummy() ) { + // do not try to get ms info in simulation mode since the cd is empty! + if( m_doc->onTheFly() ) { + m_currentAction = PREPARING_DATA; + m_isoImager->calculateSize(); + } + else + createIsoImage(); + } + else { + m_currentAction = FETCHING_MSINFO; + m_msInfoFetcher->setDevice( m_doc->burner() ); + m_msInfoFetcher->start(); + } +} + + +void K3bMixedJob::slotAudioDecoderFinished( bool success ) +{ + if( m_canceled || m_errorOccuredAndAlreadyReported ) + return; + + if( !success ) { + emit infoMessage( i18n("Error while decoding audio tracks."), ERROR ); + cleanupAfterError(); + jobFinished(false); + return; + } + + if( m_doc->onTheFly() ) { + if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) { + m_currentAction = WRITING_ISO_IMAGE; + m_isoImager->start(); + } + } + else { + emit infoMessage( i18n("Audio images successfully created."), SUCCESS ); + + if( m_doc->audioDoc()->normalize() ) { + normalizeFiles(); + } + else { + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) + m_currentAction = WRITING_ISO_IMAGE; + else + m_currentAction = WRITING_AUDIO_IMAGE; + + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + } + } +} + + +void K3bMixedJob::slotAudioDecoderNextTrack( int t, int tt ) +{ + if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) { + K3bAudioTrack* track = m_doc->audioDoc()->getTrack(t); + emit newSubTask( i18n("Decoding audio track %1 of %2%3") + .arg(t) + .arg(tt) + .arg( track->title().isEmpty() || track->artist().isEmpty() + ? QString::null + : " (" + track->artist() + " - " + track->title() + ")" ) ); + } +} + + +bool K3bMixedJob::prepareWriter() +{ + if( m_writer ) delete m_writer; + + if( ( m_currentAction == WRITING_ISO_IMAGE && m_usedDataWritingApp == K3b::CDRECORD ) || + ( m_currentAction == WRITING_AUDIO_IMAGE && m_usedAudioWritingApp == K3b::CDRECORD ) ) { + + if( !writeInfFiles() ) { + kdDebug() << "(K3bMixedJob) could not write inf-files." << endl; + emit infoMessage( i18n("IO Error"), ERROR ); + + return false; + } + + K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_doc->burner(), this, this ); + + // only write the audio tracks in DAO mode + if( m_currentAction == WRITING_ISO_IMAGE ) + writer->setWritingMode( m_usedDataWritingMode ); + else + writer->setWritingMode( m_usedAudioWritingMode ); + + writer->setSimulate( m_doc->dummy() ); + writer->setBurnSpeed( m_doc->speed() ); + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + if( m_currentAction == WRITING_ISO_IMAGE ) { + if( m_doc->onTheFly() ) + writer->addArgument("-waiti"); + + addDataTrack( writer ); + } + else { + writer->addArgument("-multi"); + addAudioTracks( writer ); + } + } + else { + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) + addDataTrack( writer ); + addAudioTracks( writer ); + if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) + addDataTrack( writer ); + } + + m_writer = writer; + } + else { + if( !writeTocFile() ) { + kdDebug() << "(K3bDataJob) could not write tocfile." << endl; + emit infoMessage( i18n("IO Error"), ERROR ); + + return false; + } + + // create the writer + // create cdrdao job + K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_doc->burner(), this, this ); + writer->setSimulate( m_doc->dummy() ); + writer->setBurnSpeed( m_doc->speed() ); + + // multisession only for the first session + writer->setMulti( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION + && m_currentAction == WRITING_AUDIO_IMAGE ); + + writer->setTocFile( m_tocFile->name() ); + + m_writer = writer; + } + + connect( m_writer, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_writer, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) ); + connect( m_writer, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) ); + connect( m_writer, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) ); + connect( m_writer, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) ); + connect( m_writer, SIGNAL(nextTrack(int, int)), this, SLOT(slotWriterNextTrack(int, int)) ); + connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) ); + connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) ); + connect( m_writer, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) ); + connect( m_writer, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) ); + // connect( m_writer, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) ); + connect( m_writer, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) ); + connect( m_writer, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); + + return true; +} + + +bool K3bMixedJob::writeInfFiles() +{ + K3bInfFileWriter infFileWriter; + K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); + while( track ) { + + infFileWriter.setTrack( track->toCdTrack() ); + infFileWriter.setTrackNumber( track->trackNumber() ); + if( !m_doc->onTheFly() ) + infFileWriter.setBigEndian( false ); + + if( !infFileWriter.save( m_tempData->infFileName(track) ) ) + return false; + + track = track->next(); + } + return true; +} + + +bool K3bMixedJob::writeTocFile() +{ + // FIXME: create the tocfile in the same directory like all the other files. + + if( m_tocFile ) delete m_tocFile; + m_tocFile = new KTempFile( QString::null, "toc" ); + m_tocFile->setAutoDelete(true); + + // write the toc-file + if( QTextStream* s = m_tocFile->textStream() ) { + + K3bTocFileWriter tocFileWriter; + + // + // TOC + // + tocFileWriter.setData( m_doc->toToc( m_usedDataMode == K3b::MODE2 + ? K3bDevice::Track::XA_FORM1 + : K3bDevice::Track::MODE1, + m_doc->onTheFly() + ? m_isoImager->size() + : m_doc->dataDoc()->length() ) ); + + // + // CD-Text + // + if( m_doc->audioDoc()->cdText() ) { + K3bDevice::CdText text = m_doc->audioDoc()->cdTextData(); + // if data in first track we need to add a dummy cdtext + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) + text.insert( text.begin(), K3bDevice::TrackCdText() ); + + tocFileWriter.setCdText( text ); + } + + // + // Session to write + // + tocFileWriter.setSession( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION && + m_currentAction == WRITING_ISO_IMAGE ? 2 : 1 ); + + // + // image filenames + // + if( !m_doc->onTheFly() ) { + QStringList files; + K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); + while( track ) { + files += m_tempData->bufferFileName( track ); + track = track->next(); + } + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) + files.prepend( m_isoImageFilePath ); + else + files.append( m_isoImageFilePath ); + + tocFileWriter.setFilenames( files ); + } + + bool success = tocFileWriter.save( *s ); + + m_tocFile->close(); + + // backup for debugging +// KIO::NetAccess::del("/tmp/trueg/tocfile_debug_backup.toc",0L); +// KIO::NetAccess::copy( m_tocFile->name(), "/tmp/trueg/tocfile_debug_backup.toc",0L ); + + return success; + } + else + return false; +} + + +void K3bMixedJob::addAudioTracks( K3bCdrecordWriter* writer ) +{ + writer->addArgument( "-useinfo" ); + + // add raw cdtext data + if( m_doc->audioDoc()->cdText() ) { + writer->setRawCdText( m_doc->audioDoc()->cdTextData().rawPackData() ); + } + + writer->addArgument( "-audio" ); + + // we always pad because although K3b makes sure all tracks' length are multiples of 2352 + // it seems that normalize sometimes corrupts these lengths + // FIXME: see K3bAudioJob for the whole less4secs and zeroPregap handling + writer->addArgument( "-pad" ); + + // Allow tracks shorter than 4 seconds + writer->addArgument( "-shorttrack" ); + + // add all the audio tracks + K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); + while( track ) { + if( m_doc->onTheFly() ) { + // this is only supported by cdrecord versions >= 2.01a13 + writer->addArgument( QFile::encodeName( m_tempData->infFileName( track ) ) ); + } + else { + writer->addArgument( QFile::encodeName( m_tempData->bufferFileName( track ) ) ); + } + track = track->next(); + } +} + +void K3bMixedJob::addDataTrack( K3bCdrecordWriter* writer ) +{ + // add data track + if( m_usedDataMode == K3b::MODE2 ) { + if( k3bcore->externalBinManager()->binObject("cdrecord") && + k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "xamix" ) ) + writer->addArgument( "-xa" ); + else + writer->addArgument( "-xa1" ); + } + else + writer->addArgument( "-data" ); + + if( m_doc->onTheFly() ) + writer->addArgument( QString("-tsize=%1s").arg(m_isoImager->size()) )->addArgument("-"); + else + writer->addArgument( m_isoImageFilePath ); +} + + +void K3bMixedJob::slotWriterNextTrack( int t, int ) +{ + K3bAudioTrack* track = 0; + + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) { + if( t > 1 ) + track = m_doc->audioDoc()->getTrack(t-1); + } + else if( m_doc->mixedType() == K3bMixedDoc::DATA_LAST_TRACK ) { + if( t < m_doc->audioDoc()->numOfTracks()+1 ) + track = m_doc->audioDoc()->getTrack(t); + } + else if( m_currentAction == WRITING_AUDIO_IMAGE ) + track = m_doc->audioDoc()->getTrack(t); + else + t = m_doc->numOfTracks(); + + if( track ) + emit newSubTask( i18n("Writing track %1 of %2%3") + .arg(t) + .arg(m_doc->numOfTracks()) + .arg( track->title().isEmpty() || track->artist().isEmpty() + ? QString::null + : " (" + track->artist() + " - " + track->title() + ")" ) ); + else + emit newSubTask( i18n("Writing track %1 of %2 (%3)").arg(t).arg(m_doc->numOfTracks()).arg(i18n("ISO9660 data")) ); +} + + +void K3bMixedJob::slotWriterJobPercent( int p ) +{ + double totalTasks = d->copies; + double tasksDone = d->copiesDone; + if( m_doc->audioDoc()->normalize() ) { + totalTasks+=1.0; + tasksDone+=1.0; + } + if( !m_doc->onTheFly() ) { + totalTasks+=1.0; + } + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + if( m_currentAction == WRITING_AUDIO_IMAGE ) { + // the audio imager has finished in all cases + // the iso imager only if this is not the first copy + if( d->copiesDone > 0 ) + tasksDone += 1.0; + else if( !m_doc->onTheFly() ) + tasksDone += m_audioDocPartOfProcess; + + p = (int)((double)p*m_audioDocPartOfProcess); + } + else { + // all images have been created + if( !m_doc->onTheFly() ) + tasksDone += 1.0; + + p = (int)(100.0*m_audioDocPartOfProcess + (double)p*(1.0-m_audioDocPartOfProcess)); + } + } + else if( !m_doc->onTheFly() ) + tasksDone += 1.0; + + emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) ); +} + + +void K3bMixedJob::slotAudioDecoderPercent( int p ) +{ + // the only thing finished here might be the isoimager which is part of this task + if( !m_doc->onTheFly() ) { + double totalTasks = d->copies+1; + if( m_doc->audioDoc()->normalize() ) + totalTasks+=1.0; + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) + p = (int)((double)p*m_audioDocPartOfProcess); + else + p = (int)(100.0*(1.0-m_audioDocPartOfProcess) + (double)p*m_audioDocPartOfProcess); + + emit percent( (int)((double)p / totalTasks) ); + } +} + + +void K3bMixedJob::slotAudioDecoderSubPercent( int p ) +{ + if( !m_doc->onTheFly() ) { + emit subPercent( p ); + } +} + + +void K3bMixedJob::slotIsoImagerPercent( int p ) +{ + if( !m_doc->onTheFly() ) { + emit subPercent( p ); + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + + double totalTasks = d->copies+1.0; + double tasksDone = d->copiesDone; + if( m_doc->audioDoc()->normalize() ) { + totalTasks+=1.0; + // the normalizer finished + tasksDone+=1.0; + } + + // the writing of the audio part finished + tasksDone += m_audioDocPartOfProcess; + + // the audio decoder finished (which is part of this task in terms of progress) + p = (int)(100.0*m_audioDocPartOfProcess + (double)p*(1.0-m_audioDocPartOfProcess)); + + emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) ); + } + else { + double totalTasks = d->copies+1.0; + if( m_doc->audioDoc()->normalize() ) + totalTasks+=1.0; + + emit percent( (int)((double)(p*(1.0-m_audioDocPartOfProcess)) / totalTasks) ); + } + } +} + + +bool K3bMixedJob::startWriting() +{ + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + if( m_currentAction == WRITING_ISO_IMAGE) { + if( m_doc->dummy() ) + emit newTask( i18n("Simulating second session") ); + else if( d->copies > 1 ) + emit newTask( i18n("Writing second session of copy %1").arg(d->copiesDone+1) ); + else + emit newTask( i18n("Writing second session") ); + } + else { + if( m_doc->dummy() ) + emit newTask( i18n("Simulating first session") ); + else if( d->copies > 1 ) + emit newTask( i18n("Writing first session of copy %1").arg(d->copiesDone+1) ); + else + emit newTask( i18n("Writing first session") ); + } + } + else if( m_doc->dummy() ) + emit newTask( i18n("Simulating") ); + else + emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) ); + + + // if we append the second session the cd is already in the drive + if( !(m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION + && m_currentAction == WRITING_ISO_IMAGE) ) { + + emit newSubTask( i18n("Waiting for media") ); + if( waitForMedia( m_doc->burner() ) < 0 ) { + cancel(); + return false; + } + + // just to be sure we did not get canceled during the async discWaiting + if( m_canceled ) + return false; + + // check if the project will fit on the CD + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + // the media is in and has been checked so this should be fast (hopefully) + K3b::Msf mediaSize = m_doc->burner()->diskInfo().capacity(); + if( mediaSize < m_projectSize ) { + if( k3bcore->globalSettings()->overburn() ) { + emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3bJob::WARNING ); + } + else { + emit infoMessage( i18n("Data does not fit on disk."), ERROR ); + return false; + } + } + } + } + + // in case we determined the max possible writing speed we have to reset the speed on the writer job + // here since an inserted media is necessary + // the Max speed job will compare the max speed value with the supported values of the writer + if( d->maxSpeed ) + m_writer->setBurnSpeed( d->maxSpeedJob->maxSpeed() ); + + emit burning(true); + m_writer->start(); + + if( m_doc->onTheFly() ) { + // now the writer is running and we can get it's stdin + // we only use this method when writing on-the-fly since + // we cannot easily change the audioDecode fd while it's working + // which we would need to do since we write into several + // image files. + m_audioImager->writeToFd( m_writer->fd() ); + m_isoImager->writeToFd( m_writer->fd() ); + } + + return true; +} + + +void K3bMixedJob::createIsoImage() +{ + m_currentAction = CREATING_ISO_IMAGE; + + // prepare iso image file + m_isoImageFilePath = m_tempFilePrefix + "_datatrack.iso"; + + if( !m_doc->onTheFly() ) + emit newTask( i18n("Creating ISO image file") ); + emit newSubTask( i18n("Creating ISO image in %1").arg(m_isoImageFilePath) ); + emit infoMessage( i18n("Creating ISO image in %1").arg(m_isoImageFilePath), INFO ); + + m_isoImager->writeToImageFile( m_isoImageFilePath ); + m_isoImager->start(); +} + + +void K3bMixedJob::cleanupAfterError() +{ + m_errorOccuredAndAlreadyReported = true; + // m_audioImager->cancel(); + m_isoImager->cancel(); + if( m_writer ) + m_writer->cancel(); + + if( m_tocFile ) delete m_tocFile; + m_tocFile = 0; + + // remove the temp files + removeBufferFiles(); +} + + +void K3bMixedJob::removeBufferFiles() +{ + if ( !m_doc->onTheFly() ) { + emit infoMessage( i18n("Removing buffer files."), INFO ); + } + + if( QFile::exists( m_isoImageFilePath ) ) + if( !QFile::remove( m_isoImageFilePath ) ) + emit infoMessage( i18n("Could not delete file %1.").arg(m_isoImageFilePath), ERROR ); + + // removes buffer images and temp toc or inf files + m_tempData->cleanup(); +} + + +void K3bMixedJob::determineWritingMode() +{ + // we don't need this when only creating image and it is possible + // that the burn device is null + if( m_doc->onlyCreateImages() ) + return; + + // at first we determine the data mode + // -------------------------------------------------------------- + if( m_doc->dataDoc()->dataMode() == K3b::DATA_MODE_AUTO ) { + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) + m_usedDataMode = K3b::MODE2; + else + m_usedDataMode = K3b::MODE1; + } + else + m_usedDataMode = m_doc->dataDoc()->dataMode(); + + + // we try to use cdrecord if possible + bool cdrecordOnTheFly = false; + bool cdrecordCdText = false; + bool cdrecordUsable = false; + + if( k3bcore->externalBinManager()->binObject("cdrecord") ) { + cdrecordOnTheFly = + k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "audio-stdin" ); + cdrecordCdText = + k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "cdtext" ); + cdrecordUsable = + !( !cdrecordOnTheFly && m_doc->onTheFly() ) && + !( m_doc->audioDoc()->cdText() && !cdrecordCdText ); + } + + // Writing Application + // -------------------------------------------------------------- + // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system! + if( writingApp() == K3b::DEFAULT ) { + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + if( m_doc->writingMode() == K3b::DAO || + ( m_doc->writingMode() == K3b::WRITING_MODE_AUTO && !cdrecordUsable ) ) { + m_usedAudioWritingApp = K3b::CDRDAO; + m_usedDataWritingApp = K3b::CDRDAO; + } + else { + m_usedAudioWritingApp = K3b::CDRECORD; + m_usedDataWritingApp = K3b::CDRECORD; + } + } + else { + if( cdrecordUsable ) { + m_usedAudioWritingApp = K3b::CDRECORD; + m_usedDataWritingApp = K3b::CDRECORD; + } + else { + m_usedAudioWritingApp = K3b::CDRDAO; + m_usedDataWritingApp = K3b::CDRDAO; + } + } + } + else { + m_usedAudioWritingApp = writingApp(); + m_usedDataWritingApp = writingApp(); + } + + // TODO: use K3bExceptions::brokenDaoAudio + + // Writing Mode (TAO/DAO/RAW) + // -------------------------------------------------------------- + if( m_doc->writingMode() == K3b::WRITING_MODE_AUTO ) { + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + if( m_usedDataWritingApp == K3b::CDRECORD ) + m_usedDataWritingMode = K3b::TAO; + else + m_usedDataWritingMode = K3b::DAO; + + // default to Session at once for the audio part + m_usedAudioWritingMode = K3b::DAO; + } + else if( writer()->dao() ) { + m_usedDataWritingMode = K3b::DAO; + m_usedAudioWritingMode = K3b::DAO; + } + else { + m_usedDataWritingMode = K3b::TAO; + m_usedAudioWritingMode = K3b::TAO; + } + } + else { + m_usedAudioWritingMode = m_doc->writingMode(); + m_usedDataWritingMode = m_doc->writingMode(); + } + + + if( m_usedDataWritingApp == K3b::CDRECORD ) { + if( !cdrecordOnTheFly && m_doc->onTheFly() ) { + m_doc->setOnTheFly( false ); + emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), ERROR ); + } + + if( m_doc->audioDoc()->cdText() ) { + if( !cdrecordCdText ) { + m_doc->audioDoc()->writeCdText( false ); + emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.").arg(k3bcore->externalBinManager()->binObject("cdrecord")->version), ERROR ); + } + else if( m_usedAudioWritingMode == K3b::TAO ) { + emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode. Try DAO or RAW."), WARNING ); + } + } + } +} + + +void K3bMixedJob::normalizeFiles() +{ + if( !m_normalizeJob ) { + m_normalizeJob = new K3bAudioNormalizeJob( this, this ); + + connect( m_normalizeJob, SIGNAL(infoMessage(const QString&, int)), + this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_normalizeJob, SIGNAL(percent(int)), this, SLOT(slotNormalizeProgress(int)) ); + connect( m_normalizeJob, SIGNAL(subPercent(int)), this, SLOT(slotNormalizeSubProgress(int)) ); + connect( m_normalizeJob, SIGNAL(finished(bool)), this, SLOT(slotNormalizeJobFinished(bool)) ); + connect( m_normalizeJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) ); + connect( m_normalizeJob, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); + } + + // add all the files + QValueVector<QString> files; + K3bAudioTrack* track = m_doc->audioDoc()->firstTrack(); + while( track ) { + files.append( m_tempData->bufferFileName(track) ); + track = track->next(); + } + + m_normalizeJob->setFilesToNormalize( files ); + + emit newTask( i18n("Normalizing volume levels") ); + m_normalizeJob->start(); +} + +void K3bMixedJob::slotNormalizeJobFinished( bool success ) +{ + if( m_canceled || m_errorOccuredAndAlreadyReported ) + return; + + if( success ) { + if( m_doc->mixedType() == K3bMixedDoc::DATA_FIRST_TRACK ) + m_currentAction = WRITING_ISO_IMAGE; + else + m_currentAction = WRITING_AUDIO_IMAGE; + + if( !prepareWriter() || !startWriting() ) { + cleanupAfterError(); + jobFinished(false); + } + } + else { + cleanupAfterError(); + jobFinished(false); + } +} + +void K3bMixedJob::slotNormalizeProgress( int p ) +{ + double totalTasks = d->copies+2.0; + double tasksDone = 0; + + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) { + // the audio imager finished (m_audioDocPartOfProcess*1 task) + // plus the normalize progress + tasksDone = m_audioDocPartOfProcess; + } + else { + // the iso and audio imagers already finished (one task) + // plus the normalize progress + tasksDone = 1.0; + } + + emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) ); +} + + +void K3bMixedJob::slotNormalizeSubProgress( int p ) +{ + emit subPercent( p ); +} + + +void K3bMixedJob::prepareProgressInformation() +{ + // calculate percentage of audio and data + // this is also used in on-the-fly mode + double ds = (double)m_doc->dataDoc()->length().totalFrames(); + double as = (double)m_doc->audioDoc()->length().totalFrames(); + m_audioDocPartOfProcess = as/(ds+as); +} + + +QString K3bMixedJob::jobDescription() const +{ + if( m_doc->mixedType() == K3bMixedDoc::DATA_SECOND_SESSION ) + return i18n("Writing Enhanced Audio CD") + + ( m_doc->audioDoc()->title().isEmpty() + ? QString::null + : QString( " (%1)" ).arg(m_doc->audioDoc()->title()) ); + else + return i18n("Writing Mixed Mode CD") + + ( m_doc->audioDoc()->title().isEmpty() + ? QString::null + : QString( " (%1)" ).arg(m_doc->audioDoc()->title()) ); +} + + +QString K3bMixedJob::jobDetails() const +{ + return ( i18n("%1 tracks (%2 minutes audio data, %3 ISO9660 data)") + .arg(m_doc->numOfTracks()) + .arg(m_doc->audioDoc()->length().toString()) + .arg(KIO::convertSize(m_doc->dataDoc()->size())) + + ( m_doc->copies() > 1 && !m_doc->dummy() + ? i18n(" - %n copy", " - %n copies", m_doc->copies()) + : QString::null ) ); +} + +#include "k3bmixedjob.moc" |