2010-11-18 00:32:46 +01:00
///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
2011-01-01 17:04:25 +01:00
// Copyright (C) 2004-2011 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
// (at your option) any later version.
//
// 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"
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>
2010-11-19 21:11:54 +01:00
2010-11-18 00:32:46 +01:00
# include <limits.h>
# include <time.h>
2010-11-20 22:14:10 +01:00
QMutex * ProcessThread : : m_mutex_genFileName = NULL ;
2010-11-18 00:32:46 +01:00
////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////
2010-12-14 01:25:13 +01:00
ProcessThread : : ProcessThread ( const AudioFileModel & audioFile , const QString & outputDirectory , 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 ) ,
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 ) ,
2010-11-19 21:11:54 +01:00
m_aborted ( false )
2010-11-18 00:32:46 +01:00
{
2010-11-20 22:14:10 +01:00
if ( m_mutex_genFileName )
{
m_mutex_genFileName = new QMutex ;
}
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
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
}
2010-11-20 02:14:22 +01:00
LAMEXP_DELETE ( m_encoder ) ;
2010-11-18 00:32:46 +01:00
}
void ProcessThread : : run ( )
2010-11-26 00:29:53 +01:00
{
try
{
processFile ( ) ;
}
catch ( . . . )
{
fflush ( stdout ) ;
fflush ( stderr ) ;
2010-12-22 01:01:01 +01:00
fprintf ( stderr , " \n GURU MEDITATION !!! \n " ) ;
2010-11-26 00:29:53 +01:00
FatalAppExit ( 0 , L " Unhandeled exception error, application will exit! " ) ;
TerminateProcess ( GetCurrentProcess ( ) , - 1 ) ;
}
}
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 ;
2010-12-19 00:50:22 +01:00
2010-11-18 22:37:35 +01:00
qDebug ( " Process thread %s has started. " , m_jobId . toString ( ) . toLatin1 ( ) . constData ( ) ) ;
2010-11-19 21:11:54 +01:00
emit processStateInitialized ( m_jobId , QFileInfo ( m_audioFile . filePath ( ) ) . fileName ( ) , " Starting... " , ProgressModel : : JobRunning ) ;
2010-11-18 00:32:46 +01:00
2010-12-01 23:14:47 +01:00
//Generate output file name
2010-11-21 21:51:22 +01:00
QString outFileName = generateOutFileName ( ) ;
if ( outFileName . isEmpty ( ) )
2010-11-20 02:14:22 +01:00
{
emit processStateChanged ( m_jobId , " Not found! " , ProgressModel : : JobFailed ) ;
2010-11-25 18:09:31 +01:00
emit processStateFinished ( m_jobId , outFileName , false ) ;
return ;
}
2010-12-19 00:50:22 +01:00
QList < AbstractFilter * > filters ;
//Do we need Stereo downmix?
if ( m_audioFile . formatAudioChannels ( ) > 2 & & m_encoder - > requiresDownmix ( ) )
{
filters . prepend ( new DownmixFilter ( ) ) ;
}
2010-12-01 23:14:47 +01:00
QString sourceFile = m_audioFile . filePath ( ) ;
//Decode source file
2010-12-19 00:50:22 +01:00
if ( ! filters . isEmpty ( ) | | ! m_encoder - > isFormatSupported ( m_audioFile . formatContainerType ( ) , m_audioFile . formatContainerProfile ( ) , m_audioFile . formatAudioType ( ) , m_audioFile . formatAudioProfile ( ) , m_audioFile . formatAudioVersion ( ) ) )
2010-11-25 18:09:31 +01:00
{
2010-12-01 23:14:47 +01:00
m_currentStep = DecodingStep ;
AbstractDecoder * decoder = DecoderRegistry : : lookup ( m_audioFile . formatContainerType ( ) , m_audioFile . formatContainerProfile ( ) , m_audioFile . formatAudioType ( ) , m_audioFile . formatAudioProfile ( ) , m_audioFile . formatAudioVersion ( ) ) ;
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 ) ;
if ( bSuccess )
{
sourceFile = tempFile ;
handleMessage ( " \n ------------------------------- \n " ) ;
}
}
else
{
handleMessage ( QString ( " The format of this file is NOT supported: \n %1 \n \n Container Format: \t %2 \n Audio Format: \t %3 " ) . arg ( m_audioFile . filePath ( ) , m_audioFile . formatContainerInfo ( ) , m_audioFile . formatAudioCompressInfo ( ) ) ) ;
emit processStateChanged ( m_jobId , " Unsupported! " , ProgressModel : : JobFailed ) ;
emit processStateFinished ( m_jobId , outFileName , false ) ;
return ;
}
2010-11-20 02:14:22 +01:00
}
2010-12-19 00:50:22 +01:00
//Apply all filters
while ( ! filters . isEmpty ( ) )
{
QString tempFile = generateTempFileName ( ) ;
AbstractFilter * poFilter = filters . takeFirst ( ) ;
if ( bSuccess )
{
connect ( poFilter , SIGNAL ( statusUpdated ( int ) ) , this , SLOT ( handleUpdate ( int ) ) , Qt : : DirectConnection ) ;
connect ( poFilter , SIGNAL ( messageLogged ( QString ) ) , this , SLOT ( handleMessage ( QString ) ) , Qt : : DirectConnection ) ;
m_currentStep = FilteringStep ;
bSuccess = poFilter - > apply ( sourceFile , tempFile , & m_aborted ) ;
if ( bSuccess )
{
sourceFile = tempFile ;
handleMessage ( " \n ------------------------------- \n " ) ;
}
}
delete poFilter ;
}
2010-12-01 23:14:47 +01:00
//Encode audio file
if ( bSuccess )
{
m_currentStep = EncodingStep ;
bSuccess = m_encoder - > encode ( sourceFile , m_audioFile , outFileName , & m_aborted ) ;
}
2010-11-20 02:14:22 +01:00
2010-12-01 23:14:47 +01:00
//Make sure output file exists
2010-11-20 02:14:22 +01:00
if ( bSuccess )
2010-11-18 00:32:46 +01:00
{
2010-11-20 22:14:10 +01:00
QFileInfo fileInfo ( outFileName ) ;
bSuccess = fileInfo . exists ( ) & & fileInfo . isFile ( ) & & ( fileInfo . size ( ) > 0 ) ;
2010-11-18 00:32:46 +01:00
}
2010-12-01 23:14:47 +01:00
//Report result
2010-11-20 02:14:22 +01:00
emit processStateChanged ( m_jobId , ( bSuccess ? " Done. " : ( m_aborted ? " Aborted! " : " Failed! " ) ) , ( bSuccess ? ProgressModel : : JobComplete : ProgressModel : : JobFailed ) ) ;
2010-11-20 22:14:10 +01:00
emit processStateFinished ( m_jobId , outFileName , bSuccess ) ;
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 )
{
2010-12-01 23:14:47 +01:00
switch ( m_currentStep )
{
case EncodingStep :
emit processStateChanged ( m_jobId , QString ( " Encoding (%1%) " ).arg(QString::number(progress)), ProgressModel::JobRunning) ;
2010-12-19 00:50:22 +01:00
break ;
case FilteringStep :
emit processStateChanged ( m_jobId , QString ( " Filtering (%1%) " ).arg(QString::number(progress)), ProgressModel::JobRunning) ;
2010-12-01 23:14:47 +01:00
break ;
case DecodingStep :
emit processStateChanged ( m_jobId , QString ( " Decoding (%1%) " ).arg(QString::number(progress)), ProgressModel::JobRunning) ;
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
////////////////////////////////////////////////////////////
QString ProcessThread : : generateOutFileName ( void )
{
2010-11-20 22:14:10 +01:00
QMutexLocker lock ( m_mutex_genFileName ) ;
2010-11-20 02:14:22 +01:00
int n = 1 ;
2010-11-21 21:51:22 +01:00
QFileInfo sourceFile ( m_audioFile . filePath ( ) ) ;
if ( ! sourceFile . exists ( ) | | ! sourceFile . isFile ( ) )
{
2010-11-25 18:09:31 +01:00
handleMessage ( QString ( " The source audio file could not be found: \n %1 " ) . arg ( sourceFile . absoluteFilePath ( ) ) ) ;
return QString ( ) ;
}
QFile readTest ( sourceFile . canonicalFilePath ( ) ) ;
if ( ! readTest . open ( QIODevice : : ReadOnly ) )
{
handleMessage ( QString ( " The source audio file could not be opened for reading: \n %1 " ) . arg ( readTest . fileName ( ) ) ) ;
2010-11-21 21:51:22 +01:00
return QString ( ) ;
}
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
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
if ( ! targetDir . exists ( ) )
{
targetDir . mkpath ( " . " ) ;
if ( ! targetDir . exists ( ) )
{
2010-11-25 18:09:31 +01:00
handleMessage ( QString ( " The target output directory doesn't exist and could NOT be created: \n %1 " ) . arg ( targetDir . absolutePath ( ) ) ) ;
2010-11-21 21:51:22 +01:00
return QString ( ) ;
}
}
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 ) )
{
handleMessage ( QString ( " The target output directory is NOT writable: \n %1 " ) . arg ( targetDir . absolutePath ( ) ) ) ;
return QString ( ) ;
}
else
{
writeTest . close ( ) ;
writeTest . remove ( ) ;
}
2010-11-26 00:29:53 +01:00
QString outFileName = QString ( " %1/%2.%3 " ) . arg ( targetDir . canonicalPath ( ) , baseName , m_encoder - > extension ( ) ) ;
2010-11-20 02:14:22 +01:00
while ( QFileInfo ( outFileName ) . exists ( ) )
{
2010-11-21 21:51:22 +01:00
outFileName = QString ( " %1/%2 (%3).%4 " ) . arg ( targetDir . canonicalPath ( ) , baseName , QString : : number ( + + n ) , m_encoder - > extension ( ) ) ;
2010-11-20 02:14:22 +01:00
}
2010-11-20 22:14:10 +01:00
QFile placeholder ( outFileName ) ;
if ( placeholder . open ( QIODevice : : WriteOnly ) )
{
placeholder . close ( ) ;
}
2010-11-20 02:14:22 +01:00
return outFileName ;
}
2010-12-01 23:14:47 +01:00
QString ProcessThread : : generateTempFileName ( void )
{
QMutexLocker lock ( m_mutex_genFileName ) ;
2010-12-07 22:58:28 +01:00
QString tempFileName = QString ( " %1/%2.wav " ) . arg ( lamexp_temp_folder ( ) , lamexp_rand_str ( ) ) ;
2010-12-01 23:14:47 +01:00
while ( QFileInfo ( tempFileName ) . exists ( ) )
{
2010-12-07 22:58:28 +01:00
tempFileName = QString ( " %1/%2.wav " ) . arg ( lamexp_temp_folder ( ) , lamexp_rand_str ( ) ) ;
2010-12-01 23:14:47 +01:00
}
QFile file ( tempFileName ) ;
if ( file . open ( QFile : : ReadWrite ) )
{
file . close ( ) ;
}
m_tempFiles < < tempFileName ;
return tempFileName ;
}
2010-11-18 00:32:46 +01:00
////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////
2010-11-20 22:14:10 +01:00
/*NONE*/