2010-11-18 00:32:46 +01:00
///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
2014-01-01 17:05:52 +01:00
// Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
2010-11-18 00:32:46 +01:00
//
// 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
2013-10-23 20:56:57 +02:00
// (at your option) any later version, but always including the *additional*
// restrictions defined in the "License.txt" file.
2010-11-18 00:32:46 +01:00
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
# include "Thread_Process.h"
# include "Global.h"
2010-11-19 21:11:54 +01:00
# include "Model_AudioFile.h"
2010-11-18 00:32:46 +01:00
# include "Model_Progress.h"
2010-11-20 02:14:22 +01:00
# include "Encoder_Abstract.h"
2010-12-01 23:14:47 +01:00
# include "Decoder_Abstract.h"
2010-12-19 00:50:22 +01:00
# include "Filter_Abstract.h"
# include "Filter_Downmix.h"
2011-05-06 17:51:49 +02:00
# include "Filter_Resample.h"
2011-12-22 00:06:34 +01:00
# include "Tool_WaveProperties.h"
2010-12-01 23:14:47 +01:00
# include "Registry_Decoder.h"
2010-11-20 02:14:22 +01:00
# include "Model_Settings.h"
2010-11-18 00:32:46 +01:00
2010-11-19 21:11:54 +01:00
# include <QUuid>
# include <QFileInfo>
2010-11-20 02:14:22 +01:00
# include <QDir>
2010-11-20 22:14:10 +01:00
# include <QMutex>
# include <QMutexLocker>
2011-03-20 23:32:11 +01:00
# include <QDate>
2013-10-09 16:11:58 +02:00
# include <QThreadPool>
2010-11-19 21:11:54 +01:00
2010-11-18 00:32:46 +01:00
# include <limits.h>
# include <time.h>
2011-05-06 17:51:49 +02:00
# include <stdlib.h>
# define DIFF(X,Y) ((X > Y) ? (X-Y) : (Y-X))
2013-10-12 22:55:41 +02:00
# define IS_WAVE(X) ((X.containerType().compare("Wave", Qt::CaseInsensitive) == 0) && (X.audioType().compare("PCM", Qt::CaseInsensitive) == 0))
2011-08-05 15:41:19 +02:00
# define STRDEF(STR,DEF) ((!STR.isEmpty()) ? STR : DEF)
2010-11-18 00:32:46 +01:00
////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////
2011-02-25 22:03:39 +01:00
ProcessThread : : ProcessThread ( const AudioFileModel & audioFile , const QString & outputDirectory , const QString & tempDirectory , AbstractEncoder * encoder , const bool prependRelativeSourcePath )
2010-11-19 21:11:54 +01:00
:
m_audioFile ( audioFile ) ,
2010-11-20 02:14:22 +01:00
m_outputDirectory ( outputDirectory ) ,
2011-02-25 22:03:39 +01:00
m_tempDirectory ( tempDirectory ) ,
2010-11-20 02:14:22 +01:00
m_encoder ( encoder ) ,
2010-11-19 21:11:54 +01:00
m_jobId ( QUuid : : createUuid ( ) ) ,
2010-12-14 01:25:13 +01:00
m_prependRelativeSourcePath ( prependRelativeSourcePath ) ,
2011-08-04 23:26:38 +02:00
m_renamePattern ( " <BaseName> " ) ,
2014-05-30 16:52:34 +02:00
m_overwriteMode ( OverwriteMode_KeepBoth ) ,
2013-10-11 20:43:31 +02:00
m_initialized ( - 1 ) ,
2011-12-22 00:06:34 +01:00
m_aborted ( false ) ,
m_propDetect ( new WaveProperties ( ) )
2010-11-18 00:32:46 +01:00
{
2010-11-20 02:14:22 +01:00
connect ( m_encoder , SIGNAL ( statusUpdated ( int ) ) , this , SLOT ( handleUpdate ( int ) ) , Qt : : DirectConnection ) ;
2010-11-22 21:45:00 +01:00
connect ( m_encoder , SIGNAL ( messageLogged ( QString ) ) , this , SLOT ( handleMessage ( QString ) ) , Qt : : DirectConnection ) ;
2010-12-01 23:14:47 +01:00
2011-12-22 00:06:34 +01:00
connect ( m_propDetect , SIGNAL ( statusUpdated ( int ) ) , this , SLOT ( handleUpdate ( int ) ) , Qt : : DirectConnection ) ;
connect ( m_propDetect , SIGNAL ( messageLogged ( QString ) ) , this , SLOT ( handleMessage ( QString ) ) , Qt : : DirectConnection ) ;
2010-12-01 23:14:47 +01:00
m_currentStep = UnknownStep ;
2010-11-18 00:32:46 +01:00
}
ProcessThread : : ~ ProcessThread ( void )
{
2010-12-01 23:14:47 +01:00
while ( ! m_tempFiles . isEmpty ( ) )
{
2010-12-21 01:09:25 +01:00
lamexp_remove_file ( m_tempFiles . takeFirst ( ) ) ;
2010-12-01 23:14:47 +01:00
}
2011-01-25 23:12:56 +01:00
while ( ! m_filters . isEmpty ( ) )
{
delete m_filters . takeFirst ( ) ;
}
2010-11-20 02:14:22 +01:00
LAMEXP_DELETE ( m_encoder ) ;
2011-12-22 00:06:34 +01:00
LAMEXP_DELETE ( m_propDetect ) ;
2013-10-09 03:14:38 +02:00
emit processFinished ( ) ;
2010-11-18 00:32:46 +01:00
}
2013-10-09 16:11:58 +02:00
////////////////////////////////////////////////////////////
// Init Function
////////////////////////////////////////////////////////////
2013-10-11 20:43:31 +02:00
bool ProcessThread : : init ( void )
2013-10-09 16:11:58 +02:00
{
2013-10-11 20:43:31 +02:00
if ( m_initialized < 0 )
2013-10-09 16:11:58 +02:00
{
2013-10-11 20:43:31 +02:00
m_initialized = 0 ;
2013-10-09 16:11:58 +02:00
//Initialize job status
qDebug ( " Process thread %s has started. " , m_jobId . toString ( ) . toLatin1 ( ) . constData ( ) ) ;
emit processStateInitialized ( m_jobId , QFileInfo ( m_audioFile . filePath ( ) ) . fileName ( ) , tr ( " Starting... " ) , ProgressModel : : JobRunning ) ;
//Initialize log
handleMessage ( QString ( ) . sprintf ( " LameXP v%u.%02u (Build #%u), compiled on %s at %s " , lamexp_version_major ( ) , lamexp_version_minor ( ) , lamexp_version_build ( ) , lamexp_version_date ( ) . toString ( Qt : : ISODate ) . toLatin1 ( ) . constData ( ) , lamexp_version_time ( ) ) ) ;
handleMessage ( " \n ------------------------------- \n " ) ;
2013-10-12 20:34:59 +02:00
return true ;
2013-10-11 20:43:31 +02:00
}
2013-10-12 20:34:59 +02:00
qWarning ( " [ProcessThread::init] Job %s already initialialized, skipping! " , m_jobId . toString ( ) . toLatin1 ( ) . constData ( ) ) ;
return false ;
2013-10-11 20:43:31 +02:00
}
bool ProcessThread : : start ( QThreadPool * pool )
{
//Make sure object was initialized correctly
if ( m_initialized < 0 )
{
2013-10-18 21:37:40 +02:00
THROW ( " Object not initialized yet! " ) ;
2013-10-11 20:43:31 +02:00
}
2013-10-09 16:11:58 +02:00
2013-10-11 20:43:31 +02:00
if ( m_initialized < 1 )
{
m_initialized = 1 ;
2013-10-09 16:11:58 +02:00
m_outFileName . clear ( ) ;
2013-10-12 20:34:59 +02:00
bool bSuccess = false ;
//Generate output file name
2013-10-09 16:11:58 +02:00
switch ( generateOutFileName ( m_outFileName ) )
{
case 1 :
//File name generated successfully :-)
2013-10-12 20:34:59 +02:00
bSuccess = true ;
2013-10-09 16:11:58 +02:00
pool - > start ( this ) ;
break ;
case - 1 :
//File name already exists -> skipping!
emit processStateChanged ( m_jobId , tr ( " Skipped. " ) , ProgressModel : : JobSkipped ) ;
emit processStateFinished ( m_jobId , m_outFileName , - 1 ) ;
break ;
default :
//File name could not be generated
emit processStateChanged ( m_jobId , tr ( " Not found! " ) , ProgressModel : : JobFailed ) ;
emit processStateFinished ( m_jobId , m_outFileName , 0 ) ;
break ;
}
2013-10-12 20:34:59 +02:00
if ( ! bSuccess )
{
emit processFinished ( ) ;
}
return bSuccess ;
2013-10-09 16:11:58 +02:00
}
2013-10-12 20:34:59 +02:00
qWarning ( " [ProcessThread::start] Job %s already started, skipping! " , m_jobId . toString ( ) . toLatin1 ( ) . constData ( ) ) ;
2013-10-09 16:11:58 +02:00
return false ;
}
2011-05-06 17:51:49 +02:00
////////////////////////////////////////////////////////////
// Thread Entry Point
////////////////////////////////////////////////////////////
2010-11-18 00:32:46 +01:00
void ProcessThread : : run ( )
2010-11-26 00:29:53 +01:00
{
try
{
processFile ( ) ;
}
2013-10-18 21:37:40 +02:00
catch ( const std : : exception & error )
{
2014-06-27 16:20:47 +02:00
PRINT_ERROR ( " \n GURU MEDITATION !!! \n \n Exception error: \n %s \n " , error . what ( ) ) ;
lamexp_fatal_exit ( " Unhandeled C++ exception error, application will exit! " ) ;
2013-10-18 21:37:40 +02:00
}
2010-11-26 00:29:53 +01:00
catch ( . . . )
{
2014-06-27 16:20:47 +02:00
PRINT_ERROR ( " \n GURU MEDITATION !!! \n \n Unknown exception error! \n " ) ;
lamexp_fatal_exit ( " Unhandeled C++ exception error, application will exit! " ) ;
2010-11-26 00:29:53 +01:00
}
}
void ProcessThread : : processFile ( )
2010-11-18 00:32:46 +01:00
{
m_aborted = false ;
2010-12-01 23:14:47 +01:00
bool bSuccess = true ;
2013-10-09 03:14:38 +02:00
2013-10-09 16:11:58 +02:00
//Make sure object was initialized correctly
2013-10-11 20:43:31 +02:00
if ( m_initialized < 1 )
2010-11-20 02:14:22 +01:00
{
2013-10-18 21:37:40 +02:00
THROW ( " Object not initialized yet! " ) ;
2010-11-25 18:09:31 +01:00
}
2011-05-06 17:51:49 +02:00
2010-12-01 23:14:47 +01:00
QString sourceFile = m_audioFile . filePath ( ) ;
2011-12-22 00:06:34 +01:00
//------------------
2010-12-01 23:14:47 +01:00
//Decode source file
2011-12-22 00:06:34 +01:00
//------------------
2013-10-12 22:55:41 +02:00
const AudioFileModel_TechInfo & formatInfo = m_audioFile . techInfo ( ) ;
if ( ! m_filters . isEmpty ( ) | | ! m_encoder - > isFormatSupported ( formatInfo . containerType ( ) , formatInfo . containerProfile ( ) , formatInfo . audioType ( ) , formatInfo . audioProfile ( ) , formatInfo . audioVersion ( ) ) )
2010-11-25 18:09:31 +01:00
{
2010-12-01 23:14:47 +01:00
m_currentStep = DecodingStep ;
2013-10-12 22:55:41 +02:00
AbstractDecoder * decoder = DecoderRegistry : : lookup ( formatInfo . containerType ( ) , formatInfo . containerProfile ( ) , formatInfo . audioType ( ) , formatInfo . audioProfile ( ) , formatInfo . audioVersion ( ) ) ;
2010-12-01 23:14:47 +01:00
if ( decoder )
{
QString tempFile = generateTempFileName ( ) ;
connect ( decoder , SIGNAL ( statusUpdated ( int ) ) , this , SLOT ( handleUpdate ( int ) ) , Qt : : DirectConnection ) ;
connect ( decoder , SIGNAL ( messageLogged ( QString ) ) , this , SLOT ( handleMessage ( QString ) ) , Qt : : DirectConnection ) ;
bSuccess = decoder - > decode ( sourceFile , tempFile , & m_aborted ) ;
2011-05-15 18:54:40 +02:00
LAMEXP_DELETE ( decoder ) ;
2010-12-01 23:14:47 +01:00
if ( bSuccess )
{
sourceFile = tempFile ;
2013-10-12 22:55:41 +02:00
m_audioFile . techInfo ( ) . setContainerType ( QString : : fromLatin1 ( " Wave " ) ) ;
m_audioFile . techInfo ( ) . setAudioType ( QString : : fromLatin1 ( " PCM " ) ) ;
2012-06-25 21:59:28 +02:00
if ( QFileInfo ( sourceFile ) . size ( ) > = 4294967296 i64 )
{
handleMessage ( tr ( " WARNING: Decoded file size exceeds 4 GB, problems might occur! \n " ) ) ;
}
handleMessage ( " \n ------------------------------- \n " ) ;
2010-12-01 23:14:47 +01:00
}
}
else
{
2013-10-09 16:11:58 +02:00
if ( QFileInfo ( m_outFileName ) . exists ( ) & & ( QFileInfo ( m_outFileName ) . size ( ) < 512 ) ) QFile : : remove ( m_outFileName ) ;
2013-10-12 22:55:41 +02:00
handleMessage ( QString ( " %1 \n %2 \n \n %3 \t %4 \n %5 \t %6 " ) . arg ( tr ( " The format of this file is NOT supported: " ) , m_audioFile . filePath ( ) , tr ( " Container Format: " ) , m_audioFile . containerInfo ( ) , tr ( " Audio Format: " ) , m_audioFile . audioCompressInfo ( ) ) ) ;
2011-01-02 01:09:05 +01:00
emit processStateChanged ( m_jobId , tr ( " Unsupported! " ) , ProgressModel : : JobFailed ) ;
2013-10-09 16:11:58 +02:00
emit processStateFinished ( m_jobId , m_outFileName , 0 ) ;
2010-12-01 23:14:47 +01:00
return ;
}
2011-12-22 18:36:41 +01:00
}
2011-12-21 01:23:21 +01:00
2011-12-22 18:36:41 +01:00
//------------------------------------
//Update audio properties after decode
//------------------------------------
2013-10-12 22:55:41 +02:00
if ( bSuccess & & ! m_aborted & & IS_WAVE ( m_audioFile . techInfo ( ) ) )
2011-12-22 18:36:41 +01:00
{
2012-07-21 19:16:12 +02:00
if ( m_encoder - > supportedSamplerates ( ) | | m_encoder - > supportedBitdepths ( ) | | m_encoder - > supportedChannelCount ( ) | | m_encoder - > needsTimingInfo ( ) | | ! m_filters . isEmpty ( ) )
2011-12-21 01:23:21 +01:00
{
2011-12-22 18:36:41 +01:00
m_currentStep = AnalyzeStep ;
2013-10-12 22:55:41 +02:00
bSuccess = m_propDetect - > detect ( sourceFile , & m_audioFile . techInfo ( ) , & m_aborted ) ;
2011-12-22 18:36:41 +01:00
if ( bSuccess )
2011-12-22 00:06:34 +01:00
{
2011-12-22 18:36:41 +01:00
handleMessage ( " \n ------------------------------- \n " ) ;
2011-12-22 00:06:34 +01:00
2011-12-22 18:36:41 +01:00
//Do we need to take care if Stereo downmix?
2011-12-23 00:43:11 +01:00
if ( m_encoder - > supportedChannelCount ( ) )
2011-12-22 00:06:34 +01:00
{
2011-12-22 18:36:41 +01:00
insertDownmixFilter ( ) ;
}
//Do we need to take care of downsampling the input?
if ( m_encoder - > supportedSamplerates ( ) | | m_encoder - > supportedBitdepths ( ) )
{
insertDownsampleFilter ( ) ;
2011-12-22 00:06:34 +01:00
}
}
2011-12-21 01:23:21 +01:00
}
}
2011-12-22 00:06:34 +01:00
//-----------------------
2011-08-07 14:04:17 +02:00
//Apply all audio filters
2011-12-22 00:06:34 +01:00
//-----------------------
2011-08-07 14:04:17 +02:00
if ( bSuccess )
2010-12-19 00:50:22 +01:00
{
2011-12-27 13:51:01 +01:00
while ( ! m_filters . isEmpty ( ) & & ! m_aborted )
2010-12-19 00:50:22 +01:00
{
2011-08-07 14:04:17 +02:00
QString tempFile = generateTempFileName ( ) ;
AbstractFilter * poFilter = m_filters . takeFirst ( ) ;
m_currentStep = FilteringStep ;
2010-12-19 00:50:22 +01:00
connect ( poFilter , SIGNAL ( statusUpdated ( int ) ) , this , SLOT ( handleUpdate ( int ) ) , Qt : : DirectConnection ) ;
connect ( poFilter , SIGNAL ( messageLogged ( QString ) ) , this , SLOT ( handleMessage ( QString ) ) , Qt : : DirectConnection ) ;
2013-10-12 22:55:41 +02:00
if ( poFilter - > apply ( sourceFile , tempFile , & m_audioFile . techInfo ( ) , & m_aborted ) )
2010-12-19 00:50:22 +01:00
{
sourceFile = tempFile ;
}
2011-08-07 14:04:17 +02:00
handleMessage ( " \n ------------------------------- \n " ) ;
delete poFilter ;
}
2010-12-19 00:50:22 +01:00
}
2011-12-22 00:06:34 +01:00
//-----------------
2010-12-01 23:14:47 +01:00
//Encode audio file
2011-12-22 00:06:34 +01:00
//-----------------
2011-12-27 13:51:01 +01:00
if ( bSuccess & & ! m_aborted )
2010-12-01 23:14:47 +01:00
{
m_currentStep = EncodingStep ;
2013-10-12 22:55:41 +02:00
bSuccess = m_encoder - > encode ( sourceFile , m_audioFile . metaInfo ( ) , m_audioFile . techInfo ( ) . duration ( ) , m_outFileName , & m_aborted ) ;
2010-12-01 23:14:47 +01:00
}
2010-11-20 02:14:22 +01:00
2012-11-11 18:58:08 +01:00
//Clean-up
if ( ( ! bSuccess ) | | m_aborted )
{
2013-10-09 16:11:58 +02:00
QFileInfo fileInfo ( m_outFileName ) ;
2012-11-11 18:58:08 +01:00
if ( fileInfo . exists ( ) & & ( fileInfo . size ( ) < 512 ) )
{
2013-10-09 16:11:58 +02:00
QFile : : remove ( m_outFileName ) ;
2012-11-11 18:58:08 +01:00
}
}
2010-12-01 23:14:47 +01:00
//Make sure output file exists
2012-11-11 18:58:08 +01:00
if ( bSuccess & & ( ! m_aborted ) )
2010-11-18 00:32:46 +01:00
{
2013-10-09 16:11:58 +02:00
QFileInfo fileInfo ( m_outFileName ) ;
2010-11-20 22:14:10 +01:00
bSuccess = fileInfo . exists ( ) & & fileInfo . isFile ( ) & & ( fileInfo . size ( ) > 0 ) ;
2010-11-18 00:32:46 +01:00
}
2013-10-09 03:14:38 +02:00
lamexp_sleep ( 125 ) ;
2011-11-16 22:56:32 +01:00
2010-12-01 23:14:47 +01:00
//Report result
2011-12-27 13:51:01 +01:00
emit processStateChanged ( m_jobId , ( m_aborted ? tr ( " Aborted! " ) : ( bSuccess ? tr ( " Done. " ) : tr ( " Failed! " ) ) ) , ( ( bSuccess & & ! m_aborted ) ? ProgressModel : : JobComplete : ProgressModel : : JobFailed ) ) ;
2013-10-09 16:11:58 +02:00
emit processStateFinished ( m_jobId , m_outFileName , ( bSuccess ? 1 : 0 ) ) ;
2010-11-20 22:14:10 +01:00
2010-11-18 22:37:35 +01:00
qDebug ( " Process thread is done. " ) ;
2010-11-18 00:32:46 +01:00
}
2010-11-20 02:14:22 +01:00
////////////////////////////////////////////////////////////
// SLOTS
////////////////////////////////////////////////////////////
void ProcessThread : : handleUpdate ( int progress )
{
2013-10-25 17:55:27 +02:00
//qDebug("Progress: %d\n", progress);
2011-11-16 22:56:32 +01:00
2010-12-01 23:14:47 +01:00
switch ( m_currentStep )
{
case EncodingStep :
2011-01-02 01:09:05 +01:00
emit processStateChanged ( m_jobId , QString ( " %1 (%2%) " ).arg(tr( " Encoding " ), QString::number(progress)), ProgressModel::JobRunning) ;
2010-12-19 00:50:22 +01:00
break ;
2011-12-22 00:06:34 +01:00
case AnalyzeStep :
emit processStateChanged ( m_jobId , QString ( " %1 (%2%) " ).arg(tr( " Analyzing " ), QString::number(progress)), ProgressModel::JobRunning) ;
break ;
2010-12-19 00:50:22 +01:00
case FilteringStep :
2011-01-02 01:09:05 +01:00
emit processStateChanged ( m_jobId , QString ( " %1 (%2%) " ).arg(tr( " Filtering " ), QString::number(progress)), ProgressModel::JobRunning) ;
2010-12-01 23:14:47 +01:00
break ;
case DecodingStep :
2011-01-02 01:09:05 +01:00
emit processStateChanged ( m_jobId , QString ( " %1 (%2%) " ).arg(tr( " Decoding " ), QString::number(progress)), ProgressModel::JobRunning) ;
2010-12-01 23:14:47 +01:00
break ;
}
2010-11-20 02:14:22 +01:00
}
2010-11-22 21:45:00 +01:00
void ProcessThread : : handleMessage ( const QString & line )
{
emit processMessageLogged ( m_jobId , line ) ;
}
2010-11-20 02:14:22 +01:00
////////////////////////////////////////////////////////////
// PRIVAE FUNCTIONS
////////////////////////////////////////////////////////////
2012-11-08 21:19:45 +01:00
int ProcessThread : : generateOutFileName ( QString & outFileName )
2010-11-20 02:14:22 +01:00
{
2012-11-08 21:19:45 +01:00
outFileName . clear ( ) ;
//Make sure the source file exists
2010-11-21 21:51:22 +01:00
QFileInfo sourceFile ( m_audioFile . filePath ( ) ) ;
2014-05-28 16:49:58 +02:00
if ( ! ( sourceFile . exists ( ) & & sourceFile . isFile ( ) ) )
2010-11-21 21:51:22 +01:00
{
2011-01-02 01:09:05 +01:00
handleMessage ( QString ( " %1 \n %2 " ) . arg ( tr ( " The source audio file could not be found: " ) , sourceFile . absoluteFilePath ( ) ) ) ;
2012-11-08 21:19:45 +01:00
return 0 ;
2010-11-25 18:09:31 +01:00
}
2012-11-08 21:19:45 +01:00
//Make sure the source file readable
2010-11-25 18:09:31 +01:00
QFile readTest ( sourceFile . canonicalFilePath ( ) ) ;
if ( ! readTest . open ( QIODevice : : ReadOnly ) )
{
2012-11-08 21:19:45 +01:00
handleMessage ( QString ( " %1 \n %2 " ) . arg ( tr ( " The source audio file could not be opened for reading: " ) , QDir : : toNativeSeparators ( readTest . fileName ( ) ) ) ) ;
return 0 ;
2010-11-21 21:51:22 +01:00
}
2010-11-25 18:09:31 +01:00
else
{
readTest . close ( ) ;
}
2010-11-20 02:14:22 +01:00
2010-11-21 21:51:22 +01:00
QString baseName = sourceFile . completeBaseName ( ) ;
QDir targetDir ( m_outputDirectory . isEmpty ( ) ? sourceFile . canonicalPath ( ) : m_outputDirectory ) ;
2010-12-14 01:25:13 +01:00
2012-11-08 21:19:45 +01:00
//Prepend relative source file path?
2010-12-14 01:25:13 +01:00
if ( m_prependRelativeSourcePath & & ! m_outputDirectory . isEmpty ( ) )
{
QDir rootDir = sourceFile . dir ( ) ;
while ( ! rootDir . isRoot ( ) )
{
if ( ! rootDir . cdUp ( ) ) break ;
}
targetDir . setPath ( QString ( " %1/%2 " ) . arg ( targetDir . absolutePath ( ) , QFileInfo ( rootDir . relativeFilePath ( sourceFile . canonicalFilePath ( ) ) ) . path ( ) ) ) ;
}
2010-11-21 21:51:22 +01:00
2012-11-08 21:19:45 +01:00
//Make sure output directory does exist
2010-11-21 21:51:22 +01:00
if ( ! targetDir . exists ( ) )
{
targetDir . mkpath ( " . " ) ;
if ( ! targetDir . exists ( ) )
{
2012-11-08 21:19:45 +01:00
handleMessage ( QString ( " %1 \n %2 " ) . arg ( tr ( " The target output directory doesn't exist and could NOT be created: " ) , QDir : : toNativeSeparators ( targetDir . absolutePath ( ) ) ) ) ;
return 0 ;
2010-11-21 21:51:22 +01:00
}
}
2012-11-08 21:19:45 +01:00
//Make sure that the output dir is writable
2010-12-07 22:58:28 +01:00
QFile writeTest ( QString ( " %1/.%2 " ) . arg ( targetDir . canonicalPath ( ) , lamexp_rand_str ( ) ) ) ;
2010-11-25 18:09:31 +01:00
if ( ! writeTest . open ( QIODevice : : ReadWrite ) )
{
2012-11-08 21:19:45 +01:00
handleMessage ( QString ( " %1 \n %2 " ) . arg ( tr ( " The target output directory is NOT writable: " ) , QDir : : toNativeSeparators ( targetDir . absolutePath ( ) ) ) ) ;
return 0 ;
2010-11-25 18:09:31 +01:00
}
else
{
writeTest . remove ( ) ;
}
2012-11-08 21:19:45 +01:00
//Apply rename pattern
2013-10-12 22:55:41 +02:00
QString fileName = applyRenamePattern ( baseName , m_audioFile . metaInfo ( ) ) ;
2011-08-04 23:26:38 +02:00
2012-11-08 21:19:45 +01:00
//Generate full output path
outFileName = QString ( " %1/%2.%3 " ) . arg ( targetDir . canonicalPath ( ) , fileName , m_encoder - > extension ( ) ) ;
//Skip file, if target file exists (optional!)
2014-05-30 16:52:34 +02:00
if ( ( m_overwriteMode = = OverwriteMode_SkipExisting ) & & QFileInfo ( outFileName ) . exists ( ) )
2012-11-08 21:19:45 +01:00
{
handleMessage ( QString ( " %1 \n %2 \n " ) . arg ( tr ( " Target output file already exists, going to skip this file: " ) , QDir : : toNativeSeparators ( outFileName ) ) ) ;
handleMessage ( tr ( " If you don't want existing files to be skipped, please change the overwrite mode! " ) ) ;
return - 1 ;
}
//Delete file, if target file exists (optional!)
2014-05-30 16:52:34 +02:00
if ( ( m_overwriteMode = = OverwriteMode_Overwrite ) & & QFileInfo ( outFileName ) . exists ( ) & & QFileInfo ( outFileName ) . isFile ( ) )
2012-11-08 21:19:45 +01:00
{
2014-05-30 16:52:34 +02:00
handleMessage ( QString ( " %1 \n %2 \n " ) . arg ( tr ( " Target output file already exists, going to delete existing file: " ) , QDir : : toNativeSeparators ( outFileName ) ) ) ;
2014-05-28 16:49:58 +02:00
if ( sourceFile . canonicalFilePath ( ) . compare ( QFileInfo ( outFileName ) . absoluteFilePath ( ) , Qt : : CaseInsensitive ) ! = 0 )
2012-11-08 21:19:45 +01:00
{
2014-05-28 16:49:58 +02:00
for ( int i = 0 ; i < 16 ; i + + )
{
if ( QFile : : remove ( outFileName ) )
{
break ;
}
lamexp_sleep ( 125 ) ;
}
2014-05-30 16:52:34 +02:00
}
if ( QFileInfo ( outFileName ) . exists ( ) )
{
handleMessage ( QString ( " %1 \n " ) . arg ( tr ( " Failed to delete existing target file, will save to another file name! " ) ) ) ;
2012-11-08 21:19:45 +01:00
}
}
int n = 1 ;
//Generate final name
2014-05-28 16:49:58 +02:00
while ( QFileInfo ( outFileName ) . exists ( ) & & ( n < ( INT_MAX / 2 ) ) )
2010-11-20 02:14:22 +01:00
{
2011-08-04 23:26:38 +02:00
outFileName = QString ( " %1/%2 (%3).%4 " ) . arg ( targetDir . canonicalPath ( ) , fileName , QString : : number ( + + n ) , m_encoder - > extension ( ) ) ;
2010-11-20 02:14:22 +01:00
}
2012-11-08 21:19:45 +01:00
//Create placeholder
2010-11-20 22:14:10 +01:00
QFile placeholder ( outFileName ) ;
if ( placeholder . open ( QIODevice : : WriteOnly ) )
{
placeholder . close ( ) ;
}
2012-11-08 21:19:45 +01:00
return 1 ;
2010-11-20 02:14:22 +01:00
}
2013-10-12 22:55:41 +02:00
QString ProcessThread : : applyRenamePattern ( const QString & baseName , const AudioFileModel_MetaInfo & metaInfo )
{
QString fileName = m_renamePattern ;
fileName . replace ( " <BaseName> " , STRDEF ( baseName , tr ( " Unknown File Name " ) ) , Qt : : CaseInsensitive ) ;
fileName . replace ( " <TrackNo> " , QString ( ) . sprintf ( " %02d " , metaInfo . position ( ) ) , Qt : : CaseInsensitive ) ;
fileName . replace ( " <Title> " , STRDEF ( metaInfo . title ( ) , tr ( " Unknown Title " ) ) , Qt : : CaseInsensitive ) ;
fileName . replace ( " <Artist> " , STRDEF ( metaInfo . artist ( ) , tr ( " Unknown Artist " ) ) , Qt : : CaseInsensitive ) ;
fileName . replace ( " <Album> " , STRDEF ( metaInfo . album ( ) , tr ( " Unknown Album " ) ) , Qt : : CaseInsensitive ) ;
fileName . replace ( " <Year> " , QString ( ) . sprintf ( " %04d " , metaInfo . year ( ) ) , Qt : : CaseInsensitive ) ;
fileName . replace ( " <Comment> " , STRDEF ( metaInfo . comment ( ) , tr ( " Unknown Comment " ) ) , Qt : : CaseInsensitive ) ;
fileName = lamexp_clean_filename ( fileName ) . simplified ( ) ;
return fileName ;
}
2010-12-01 23:14:47 +01:00
QString ProcessThread : : generateTempFileName ( void )
{
2013-10-09 16:11:58 +02:00
bool bOkay = false ;
QString tempFileName ;
for ( int i = 0 ; i < 4096 ; i + + )
2010-12-01 23:14:47 +01:00
{
2011-02-25 22:03:39 +01:00
tempFileName = QString ( " %1/%2.wav " ) . arg ( m_tempDirectory , lamexp_rand_str ( ) ) ;
2013-10-09 16:11:58 +02:00
if ( m_tempFiles . contains ( tempFileName , Qt : : CaseInsensitive ) | | QFileInfo ( tempFileName ) . exists ( ) )
{
continue ;
}
QFile file ( tempFileName ) ;
if ( file . open ( QFile : : ReadWrite ) )
{
file . close ( ) ;
bOkay = true ;
break ;
}
2010-12-01 23:14:47 +01:00
}
2013-10-09 16:11:58 +02:00
if ( ! bOkay )
2010-12-01 23:14:47 +01:00
{
2013-10-09 16:11:58 +02:00
qWarning ( " Failed to generate unique temp file name! " ) ;
return QString ( " %1/~whoops.wav " ) . arg ( m_tempDirectory ) ;
2010-12-01 23:14:47 +01:00
}
m_tempFiles < < tempFileName ;
return tempFileName ;
}
2011-05-06 17:51:49 +02:00
void ProcessThread : : insertDownsampleFilter ( void )
{
2011-12-22 18:36:41 +01:00
int targetSampleRate = 0 ;
int targetBitDepth = 0 ;
/* Adjust sample rate */
2013-10-12 22:55:41 +02:00
if ( m_encoder - > supportedSamplerates ( ) & & m_audioFile . techInfo ( ) . audioSamplerate ( ) )
2011-05-06 17:51:49 +02:00
{
2011-12-22 18:36:41 +01:00
bool applyDownsampling = true ;
//Check if downsampling filter is already in the chain
for ( int i = 0 ; i < m_filters . count ( ) ; i + + )
2011-05-06 17:51:49 +02:00
{
2011-12-22 18:36:41 +01:00
if ( dynamic_cast < ResampleFilter * > ( m_filters . at ( i ) ) )
{
qWarning ( " Encoder requires downsampling, but user has already set resamling filter! " ) ;
handleMessage ( " WARNING: Encoder may need resampling, but already using resample filter. Encoding *may* fail! \n " ) ;
applyDownsampling = false ;
}
2011-05-06 17:51:49 +02:00
}
2011-12-22 18:36:41 +01:00
//Now determine the target sample rate, if required
if ( applyDownsampling )
{
const unsigned int * supportedRates = m_encoder - > supportedSamplerates ( ) ;
2013-10-12 22:55:41 +02:00
const unsigned int inputRate = m_audioFile . techInfo ( ) . audioSamplerate ( ) ;
2011-12-22 18:36:41 +01:00
unsigned int currentDiff = UINT_MAX , minimumDiff = UINT_MAX , bestRate = UINT_MAX ;
//Find the most suitable supported sampling rate
for ( int i = 0 ; supportedRates [ i ] ; i + + )
{
currentDiff = DIFF ( inputRate , supportedRates [ i ] ) ;
2011-12-22 21:53:25 +01:00
if ( ( currentDiff < minimumDiff ) | | ( ( currentDiff = = minimumDiff ) & & ( bestRate < supportedRates [ i ] ) ) )
2011-12-22 18:36:41 +01:00
{
bestRate = supportedRates [ i ] ;
minimumDiff = currentDiff ;
if ( ! ( minimumDiff > 0 ) ) break ;
}
}
if ( bestRate ! = inputRate )
{
targetSampleRate = ( bestRate ! = UINT_MAX ) ? bestRate : supportedRates [ 0 ] ;
}
}
}
/* Adjust bit depth (word size) */
2013-10-12 22:55:41 +02:00
if ( m_encoder - > supportedBitdepths ( ) & & m_audioFile . techInfo ( ) . audioBitdepth ( ) )
2011-05-06 17:51:49 +02:00
{
2013-10-12 22:55:41 +02:00
const unsigned int inputBPS = m_audioFile . techInfo ( ) . audioBitdepth ( ) ;
2012-08-22 23:52:55 +02:00
const unsigned int * supportedBPS = m_encoder - > supportedBitdepths ( ) ;
2011-05-06 17:51:49 +02:00
2012-08-22 23:52:55 +02:00
bool bAdjustBitdepth = true ;
2013-10-23 20:56:57 +02:00
//Is the input bit depth supported exactly? (including IEEE Float)
2011-12-22 18:36:41 +01:00
for ( int i = 0 ; supportedBPS [ i ] ; i + + )
2011-05-06 17:51:49 +02:00
{
2012-08-22 23:52:55 +02:00
if ( supportedBPS [ i ] = = inputBPS ) bAdjustBitdepth = false ;
}
if ( bAdjustBitdepth )
{
unsigned int currentDiff = UINT_MAX , minimumDiff = UINT_MAX , bestBPS = UINT_MAX ;
const unsigned int originalBPS = ( inputBPS = = AudioFileModel : : BITDEPTH_IEEE_FLOAT32 ) ? 32 : inputBPS ;
//Find the most suitable supported bit depth
for ( int i = 0 ; supportedBPS [ i ] ; i + + )
2011-05-06 17:51:49 +02:00
{
2012-08-22 23:52:55 +02:00
if ( supportedBPS [ i ] = = AudioFileModel : : BITDEPTH_IEEE_FLOAT32 ) continue ;
currentDiff = DIFF ( originalBPS , supportedBPS [ i ] ) ;
if ( ( currentDiff < minimumDiff ) | | ( ( currentDiff = = minimumDiff ) & & ( bestBPS < supportedBPS [ i ] ) ) )
{
bestBPS = supportedBPS [ i ] ;
minimumDiff = currentDiff ;
if ( ! ( minimumDiff > 0 ) ) break ;
}
2011-05-06 17:51:49 +02:00
}
2011-12-22 18:36:41 +01:00
2012-08-22 23:52:55 +02:00
if ( bestBPS ! = originalBPS )
{
targetBitDepth = ( bestBPS ! = UINT_MAX ) ? bestBPS : supportedBPS [ 0 ] ;
}
2011-05-06 17:51:49 +02:00
}
}
2011-12-22 18:36:41 +01:00
/* Insert the filter */
if ( targetSampleRate | | targetBitDepth )
{
m_filters . append ( new ResampleFilter ( targetSampleRate , targetBitDepth ) ) ;
}
2011-12-21 01:23:21 +01:00
}
2011-08-06 18:56:09 +02:00
void ProcessThread : : insertDownmixFilter ( void )
{
bool applyDownmixing = true ;
//Check if downmixing filter is already in the chain
for ( int i = 0 ; i < m_filters . count ( ) ; i + + )
{
if ( dynamic_cast < DownmixFilter * > ( m_filters . at ( i ) ) )
{
qWarning ( " Encoder requires Stereo downmix, but user has already forced downmix! " ) ;
2011-12-23 00:43:11 +01:00
handleMessage ( " WARNING: Encoder may need downmixning, but already using downmixning filter. Encoding *may* fail! \n " ) ;
2011-08-06 18:56:09 +02:00
applyDownmixing = false ;
}
}
2011-08-27 21:28:20 +02:00
2011-08-06 18:56:09 +02:00
//Now add the downmixing filter, if needed
if ( applyDownmixing )
{
2011-12-23 00:43:11 +01:00
bool requiresDownmix = true ;
const unsigned int * supportedChannels = m_encoder - > supportedChannelCount ( ) ;
2013-10-12 22:55:41 +02:00
unsigned int channels = m_audioFile . techInfo ( ) . audioChannels ( ) ;
2011-12-23 00:43:11 +01:00
for ( int i = 0 ; supportedChannels [ i ] ; i + + )
{
if ( supportedChannels [ i ] = = channels )
{
requiresDownmix = false ;
break ;
}
}
if ( requiresDownmix )
2011-08-27 21:28:20 +02:00
{
2011-12-22 18:36:41 +01:00
m_filters . append ( new DownmixFilter ( ) ) ;
2011-08-27 21:28:20 +02:00
}
2011-08-06 18:56:09 +02:00
}
}
2011-05-06 17:51:49 +02:00
////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////
2011-01-25 23:12:56 +01:00
void ProcessThread : : addFilter ( AbstractFilter * filter )
{
m_filters . append ( filter ) ;
}
2011-08-04 23:26:38 +02:00
void ProcessThread : : setRenamePattern ( const QString & pattern )
{
2011-08-05 02:33:32 +02:00
QString newPattern = pattern . simplified ( ) ;
if ( ! newPattern . isEmpty ( ) ) m_renamePattern = newPattern ;
2011-08-04 23:26:38 +02:00
}
2014-05-30 16:52:34 +02:00
void ProcessThread : : setOverwriteMode ( const bool & bSkipExistingFile , const bool & bReplacesExisting )
2012-11-08 21:19:45 +01:00
{
if ( bSkipExistingFile & & bReplacesExisting )
{
2014-05-30 16:52:34 +02:00
qWarning ( " Inconsistent overwrite flags -> reverting to default! " ) ;
m_overwriteMode = OverwriteMode_KeepBoth ;
}
else
{
m_overwriteMode = OverwriteMode_KeepBoth ;
if ( bSkipExistingFile ) m_overwriteMode = OverwriteMode_SkipExisting ;
if ( bReplacesExisting ) m_overwriteMode = OverwriteMode_Overwrite ;
2012-11-08 21:19:45 +01:00
}
}
2010-11-18 00:32:46 +01:00
////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////
2011-11-16 22:56:32 +01:00
/*NONE*/