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