summaryrefslogtreecommitdiffstats
path: root/libk3b/projects/audiocd/k3baudiojob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libk3b/projects/audiocd/k3baudiojob.cpp')
-rw-r--r--libk3b/projects/audiocd/k3baudiojob.cpp864
1 files changed, 864 insertions, 0 deletions
diff --git a/libk3b/projects/audiocd/k3baudiojob.cpp b/libk3b/projects/audiocd/k3baudiojob.cpp
new file mode 100644
index 0000000..c2e62c2
--- /dev/null
+++ b/libk3b/projects/audiocd/k3baudiojob.cpp
@@ -0,0 +1,864 @@
+/*
+ *
+ * $Id: k3baudiojob.cpp 690212 2007-07-20 11:02:13Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * 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 "k3baudiojob.h"
+
+#include "k3baudioimager.h"
+#include <k3baudiodoc.h>
+#include "k3baudiotrack.h"
+#include "k3baudiodatasource.h"
+#include "k3baudionormalizejob.h"
+#include "k3baudiojobtempdata.h"
+#include "k3baudiomaxspeedjob.h"
+#include "k3baudiocdtracksource.h"
+#include "k3baudiofile.h"
+#include <k3bdevicemanager.h>
+#include <k3bdevicehandler.h>
+#include <k3bdevice.h>
+#include <k3bcdtext.h>
+#include <k3bmsf.h>
+#include <k3bglobals.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+#include <k3btocfilewriter.h>
+#include <k3binffilewriter.h>
+
+#include <qfile.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <ktempfile.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 K3bAudioJob::Private
+{
+ public:
+ Private()
+ : copies(1),
+ copiesDone(0) {
+ }
+
+ int copies;
+ int copiesDone;
+ int usedSpeed;
+
+ bool useCdText;
+ bool maxSpeed;
+
+ bool zeroPregap;
+ bool less4Sec;
+};
+
+
+K3bAudioJob::K3bAudioJob( K3bAudioDoc* doc, K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent ),
+ m_doc( doc ),
+ m_normalizeJob(0),
+ m_maxSpeedJob(0)
+{
+ d = new Private;
+
+ m_audioImager = new K3bAudioImager( m_doc, 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_writer = 0;
+ m_tempData = new K3bAudioJobTempData( m_doc, this );
+}
+
+
+K3bAudioJob::~K3bAudioJob()
+{
+ delete d;
+}
+
+
+K3bDevice::Device* K3bAudioJob::writer() const
+{
+ if( m_doc->onlyCreateImages() )
+ return 0; // no writer needed -> no blocking on K3bBurnJob
+ else
+ return m_doc->burner();
+}
+
+
+K3bDoc* K3bAudioJob::doc() const
+{
+ return m_doc;
+}
+
+
+void K3bAudioJob::start()
+{
+ jobStarted();
+
+ m_written = true;
+ m_canceled = false;
+ m_errorOccuredAndAlreadyReported = false;
+ d->copies = m_doc->copies();
+ d->copiesDone = 0;
+ d->useCdText = m_doc->cdText();
+ d->usedSpeed = m_doc->speed();
+ d->maxSpeed = false;
+
+ if( m_doc->dummy() )
+ d->copies = 1;
+
+ emit newTask( i18n("Preparing data") );
+
+ //
+ // Check if all files exist
+ //
+ QValueList<K3bAudioFile*> nonExistingFiles;
+ K3bAudioTrack* track = m_doc->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->numOfTracks() == 0 ) {
+ emit infoMessage( i18n("Please add files to your project first."), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ if( m_doc->onTheFly() && !checkAudioSources() ) {
+ emit infoMessage( i18n("Unable to write on-the-fly with these audio sources."), WARNING );
+ m_doc->setOnTheFly(false);
+ }
+
+
+ // we don't need this when only creating image and it is possible
+ // that the burn device is null
+ if( !m_doc->onlyCreateImages() ) {
+
+ //
+ // there are a lot of writers out there which produce coasters
+ // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?)
+ //
+ // Also most writers do not accept cuesheets with tracks smaller than 4 seconds (a violation
+ // of the red book standard) in DAO mode.
+ //
+ d->zeroPregap = false;
+ d->less4Sec = false;
+ track = m_doc->firstTrack();
+ while( track ) {
+ if( track->postGap() == 0 && track->next() != 0 ) // the last track's postgap is always 0
+ d->zeroPregap = true;
+
+ if( track->length() < K3b::Msf( 0, 4, 0 ) )
+ d->less4Sec = true;
+
+ track = track->next();
+ }
+
+ // determine writing mode
+ if( m_doc->writingMode() == K3b::WRITING_MODE_AUTO ) {
+ //
+ // DAO is always the first choice
+ // RAW second and TAO last
+ // there are none-DAO writers that are supported by cdrdao
+ //
+ // older cdrecord versions do not support the -shorttrack option in RAW writing mode
+ //
+ if( !writer()->dao() && writingApp() == K3b::CDRECORD ) {
+ if(!writer()->supportsRawWriting() &&
+ ( !d->less4Sec || k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "short-track-raw" ) ) )
+ m_usedWritingMode = K3b::RAW;
+ else
+ m_usedWritingMode = K3b::TAO;
+ }
+ else {
+ if( (d->zeroPregap||d->less4Sec) && writer()->supportsRawWriting() ) {
+ m_usedWritingMode = K3b::RAW;
+ if( d->less4Sec )
+ emit infoMessage( i18n("Tracklengths below 4 seconds violate the Red Book standard."), WARNING );
+ }
+ else
+ m_usedWritingMode = K3b::DAO;
+ }
+ }
+ else
+ m_usedWritingMode = m_doc->writingMode();
+
+ bool cdrecordOnTheFly = false;
+ bool cdrecordCdText = false;
+ if( k3bcore->externalBinManager()->binObject("cdrecord") ) {
+ cdrecordOnTheFly = k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "audio-stdin" );
+ cdrecordCdText = k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "cdtext" );
+ }
+
+ // determine writing app
+ if( writingApp() == K3b::DEFAULT ) {
+ if( m_usedWritingMode == K3b::DAO ) {
+ // there are none-DAO writers that are supported by cdrdao
+ if( !writer()->dao() ||
+ ( !cdrecordOnTheFly && m_doc->onTheFly() ) ||
+ ( d->useCdText && !cdrecordCdText ) ||
+ m_doc->hideFirstTrack() )
+ m_usedWritingApp = K3b::CDRDAO;
+ else
+ m_usedWritingApp = K3b::CDRECORD;
+ }
+ else
+ m_usedWritingApp = K3b::CDRECORD;
+ }
+ else
+ m_usedWritingApp = writingApp();
+
+ // on-the-fly writing with cdrecord >= 2.01a13
+ if( m_usedWritingApp == K3b::CDRECORD &&
+ m_doc->onTheFly() &&
+ !cdrecordOnTheFly ) {
+ emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), ERROR );
+ m_doc->setOnTheFly(false);
+ }
+
+ if( m_usedWritingApp == K3b::CDRECORD &&
+ d->useCdText ) {
+ if( !cdrecordCdText ) {
+ emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.")
+ .arg(k3bcore->externalBinManager()->binObject("cdrecord")->version), ERROR );
+ d->useCdText = false;
+ }
+ else if( m_usedWritingMode == K3b::TAO ) {
+ emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), WARNING );
+ d->useCdText = false;
+ }
+ }
+ }
+
+
+ if( !m_doc->onlyCreateImages() && m_doc->onTheFly() ) {
+ if( m_doc->speed() == 0 ) {
+ // try to determine the max possible speed
+ emit newSubTask( i18n("Determining maximum writing speed") );
+ if( !m_maxSpeedJob ) {
+ m_maxSpeedJob = new K3bAudioMaxSpeedJob( m_doc, this, this );
+ connect( m_maxSpeedJob, SIGNAL(percent(int)),
+ this, SIGNAL(subPercent(int)) );
+ connect( m_maxSpeedJob, SIGNAL(finished(bool)),
+ this, SLOT(slotMaxSpeedJobFinished(bool)) );
+ }
+ m_maxSpeedJob->start();
+ return;
+ }
+ else {
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( startWriting() ) {
+
+ // 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() );
+ }
+ else {
+ // startWriting() already did the cleanup
+ return;
+ }
+ }
+ }
+ else {
+ emit burning(false);
+ emit infoMessage( i18n("Creating image files in %1").arg(m_doc->tempDir()), INFO );
+ emit newTask( i18n("Creating image files") );
+ m_tempData->prepareTempFileNames( doc()->tempDir() );
+ QStringList filenames;
+ for( int i = 1; i <= m_doc->numOfTracks(); ++i )
+ filenames += m_tempData->bufferFileName( i );
+ m_audioImager->setImageFilenames( filenames );
+ }
+
+ m_audioImager->start();
+}
+
+
+void K3bAudioJob::slotMaxSpeedJobFinished( bool success )
+{
+ d->maxSpeed = success;
+ if( !success )
+ emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), WARNING );
+
+ // now start the writing
+ // same code as above. See the commecnts there
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( startWriting() )
+ m_audioImager->writeToFd( m_writer->fd() );
+
+ m_audioImager->start();
+}
+
+
+void K3bAudioJob::cancel()
+{
+ m_canceled = true;
+
+ if( m_maxSpeedJob )
+ m_maxSpeedJob->cancel();
+
+ if( m_writer )
+ m_writer->cancel();
+
+ m_audioImager->cancel();
+ emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR );
+ removeBufferFiles();
+ emit canceled();
+ jobFinished(false);
+}
+
+
+void K3bAudioJob::slotWriterFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( !success ) {
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+ else {
+ d->copiesDone++;
+
+ if( d->copiesDone == d->copies ) {
+ if( m_doc->onTheFly() || m_doc->removeImages() )
+ removeBufferFiles();
+
+ jobFinished(true);
+ }
+ else {
+ K3bDevice::eject( m_doc->burner() );
+
+ if( startWriting() ) {
+ 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_audioImager->start();
+ }
+ }
+ }
+ }
+}
+
+
+void K3bAudioJob::slotAudioDecoderFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( !success ) {
+ if( m_audioImager->lastErrorType() == K3bAudioImager::ERROR_FD_WRITE ) {
+ // this means that the writer job failed so let's use the error handling there.
+ return;
+ }
+
+ emit infoMessage( i18n("Error while decoding audio tracks."), ERROR );
+ cleanupAfterError();
+ jobFinished(false);
+ return;
+ }
+
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+
+ emit infoMessage( i18n("Successfully decoded all tracks."), SUCCESS );
+
+ if( m_doc->normalize() ) {
+ normalizeFiles();
+ }
+ else if( !m_doc->onlyCreateImages() ) {
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else
+ startWriting();
+ }
+ else {
+ jobFinished(true);
+ }
+ }
+}
+
+
+void K3bAudioJob::slotAudioDecoderNextTrack( int t, int tt )
+{
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+ K3bAudioTrack* track = m_doc->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 K3bAudioJob::prepareWriter()
+{
+ delete m_writer;
+
+ if( m_usedWritingApp == K3b::CDRECORD ) {
+
+ if( !writeInfFiles() ) {
+ kdDebug() << "(K3bAudioJob) could not write inf-files." << endl;
+ emit infoMessage( i18n("IO Error. Most likely no space left on harddisk."), ERROR );
+
+ return false;
+ }
+
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_doc->burner(), this, this );
+
+ writer->setWritingMode( m_usedWritingMode );
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( d->usedSpeed );
+
+ writer->addArgument( "-useinfo" );
+
+ if( d->useCdText ) {
+ writer->setRawCdText( m_doc->cdTextData().rawPackData() );
+ }
+
+ // add all the audio tracks
+ writer->addArgument( "-audio" );
+
+ // we only need to pad in one case. cdrecord < 2.01.01a03 cannot handle shorttrack + raw
+ if( d->less4Sec ) {
+ if( m_usedWritingMode == K3b::RAW &&
+ !k3bcore->externalBinManager()->binObject( "cdrecord" )->hasFeature( "short-track-raw" ) ) {
+ writer->addArgument( "-pad" );
+ }
+ else {
+ // Allow tracks shorter than 4 seconds
+ writer->addArgument( "-shorttrack" );
+ }
+ }
+
+ K3bAudioTrack* track = m_doc->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();
+ }
+
+ 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->setCommand( K3bCdrdaoWriter::WRITE );
+ writer->setSimulate( m_doc->dummy() );
+ writer->setBurnSpeed( d->usedSpeed );
+ writer->setTocFile( m_tempData->tocFileName() );
+
+ 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;
+}
+
+
+void K3bAudioJob::slotWriterNextTrack( int t, int tt )
+{
+ K3bAudioTrack* track = m_doc->getTrack(t);
+ // t is in range 1..tt
+ if( m_doc->hideFirstTrack() )
+ track = m_doc->getTrack(t+1);
+ emit newSubTask( i18n("Writing track %1 of %2%3")
+ .arg(t)
+ .arg(tt)
+ .arg( track->title().isEmpty() || track->artist().isEmpty()
+ ? QString::null
+ : " (" + track->artist() + " - " + track->title() + ")" ) );
+}
+
+
+void K3bAudioJob::slotWriterJobPercent( int p )
+{
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone;
+ if( m_doc->normalize() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+ if( !m_doc->onTheFly() ) {
+ totalTasks+=1.0;
+ tasksDone+=1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bAudioJob::slotAudioDecoderPercent( int p )
+{
+ if( m_doc->onlyCreateImages() ) {
+ if( m_doc->normalize() )
+ emit percent( p/2 );
+ else
+ emit percent( p );
+ }
+ else if( !m_doc->onTheFly() ) {
+ double totalTasks = d->copies;
+ double tasksDone = d->copiesDone; // =0 when creating an image
+ if( m_doc->normalize() ) {
+ totalTasks+=1.0;
+ }
+ if( !m_doc->onTheFly() ) {
+ totalTasks+=1.0;
+ }
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+ }
+}
+
+
+void K3bAudioJob::slotAudioDecoderSubPercent( int p )
+{
+ // when writing on the fly the writer produces the subPercent
+ if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
+ emit subPercent( p );
+ }
+}
+
+
+bool K3bAudioJob::startWriting()
+{
+ if( m_doc->dummy() )
+ emit newTask( i18n("Simulating") );
+ else if( d->copies > 1 )
+ emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) );
+ else
+ emit newTask( i18n("Writing") );
+
+
+ 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;
+
+ // 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( m_maxSpeedJob->maxSpeed() );
+
+ emit burning(true);
+ m_writer->start();
+ return true;
+}
+
+
+void K3bAudioJob::cleanupAfterError()
+{
+ m_errorOccuredAndAlreadyReported = true;
+ m_audioImager->cancel();
+
+ if( m_writer )
+ m_writer->cancel();
+
+ // remove the temp files
+ removeBufferFiles();
+}
+
+
+void K3bAudioJob::removeBufferFiles()
+{
+ if ( !m_doc->onTheFly() ) {
+ emit infoMessage( i18n("Removing temporary files."), INFO );
+ }
+
+ // removes buffer images and temp toc or inf files
+ m_tempData->cleanup();
+}
+
+
+void K3bAudioJob::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
+ // TODO: we may need to split the wave files and put them back together!
+ QValueVector<QString> files;
+ K3bAudioTrack* track = m_doc->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 K3bAudioJob::slotNormalizeJobFinished( bool success )
+{
+ if( m_canceled || m_errorOccuredAndAlreadyReported )
+ return;
+
+ if( success ) {
+ if( m_doc->onlyCreateImages() ) {
+ jobFinished(true);
+ }
+ else {
+ // start the writing
+ if( !prepareWriter() ) {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+ else
+ startWriting();
+ }
+ }
+ else {
+ cleanupAfterError();
+ jobFinished(false);
+ }
+}
+
+void K3bAudioJob::slotNormalizeProgress( int p )
+{
+ double totalTasks = d->copies+2.0;
+ double tasksDone = 1; // the decoding has been finished
+
+ emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
+}
+
+
+void K3bAudioJob::slotNormalizeSubProgress( int p )
+{
+ emit subPercent( p );
+}
+
+
+bool K3bAudioJob::writeTocFile()
+{
+ K3bTocFileWriter tocWriter;
+ tocWriter.setData( m_doc->toToc() );
+ tocWriter.setHideFirstTrack( m_doc->hideFirstTrack() );
+ if( d->useCdText )
+ tocWriter.setCdText( m_doc->cdTextData() );
+ if( !m_doc->onTheFly() ) {
+ QStringList filenames;
+ for( int i = 1; i <= m_doc->numOfTracks(); ++i )
+ filenames += m_tempData->bufferFileName( i );
+ tocWriter.setFilenames( filenames );
+ }
+ return tocWriter.save( m_tempData->tocFileName() );
+}
+
+
+bool K3bAudioJob::writeInfFiles()
+{
+ K3bInfFileWriter infFileWriter;
+ K3bAudioTrack* track = m_doc->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;
+}
+
+
+// checks if the doc contains sources from an audio cd which cannot be read on-the-fly
+bool K3bAudioJob::checkAudioSources()
+{
+ K3bAudioTrack* track = m_doc->firstTrack();
+ K3bAudioDataSource* source = track->firstSource();
+
+ while( source ) {
+
+ if( K3bAudioCdTrackSource* cdSource = dynamic_cast<K3bAudioCdTrackSource*>(source) ) {
+ //
+ // If which cases we cannot wite on-the-fly:
+ // 1. the writing device contains one of the audio cds
+ // 2. Well, one of the cds is missing
+ //
+ K3bDevice::Device* dev = cdSource->searchForAudioCD();
+ if( !dev || dev == writer() )
+ return false;
+ else
+ cdSource->setDevice( dev );
+ }
+
+ // next source
+ source = source->next();
+ if( !source ) {
+ track = track->next();
+ if( track )
+ source = track->firstSource();
+ }
+ }
+
+ return true;
+}
+
+
+QString K3bAudioJob::jobDescription() const
+{
+ return i18n("Writing Audio CD")
+ + ( m_doc->title().isEmpty()
+ ? QString::null
+ : QString( " (%1)" ).arg(m_doc->title()) );
+}
+
+
+QString K3bAudioJob::jobDetails() const
+{
+ return ( i18n( "1 track (%1 minutes)",
+ "%n tracks (%1 minutes)",
+ m_doc->numOfTracks() ).arg(m_doc->length().toString())
+ + ( m_doc->copies() > 1 && !m_doc->dummy()
+ ? i18n(" - %n copy", " - %n copies", m_doc->copies())
+ : QString::null ) );
+}
+
+#include "k3baudiojob.moc"