<translation type="unfinished"></translation>
</message>
<message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
<source>An unexpected error has occured while splitting the Cue Sheet!</source>
<translation type="unfinished"></translation>
</message>
<source>Abort</source>
<translation>Abbrechen</translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation>Der Vorgang wurde vom Benutzter nach %1 Track(s) abgebrochen!</translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
<source>Abort</source>
<translation type="unfinished">Cancelar</translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
<source>Abort</source>
<translation type="unfinished">Abandonner</translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
<source>Abort</source>
<translation type="unfinished">Ferma</translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
<source>Abort</source>
<translation type="unfinished">취소</translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
<source>Abort</source>
<translation type="unfinished">Отмена</translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
<source>Abort</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <source>Process was aborted by the user after %1 track(s)!</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>CueSheetImport</name>
#define VER_LAMEXP_MAJOR 4
#define VER_LAMEXP_MINOR_HI 0
#define VER_LAMEXP_MINOR_LO 2
-#define VER_LAMEXP_TYPE Alpha
-#define VER_LAMEXP_PATCH 15
-#define VER_LAMEXP_BUILD 526
+#define VER_LAMEXP_TYPE Beta
+#define VER_LAMEXP_PATCH 1
+#define VER_LAMEXP_BUILD 528
///////////////////////////////////////////////////////////////////////////////
// Tools versions
}
m_outputDir = QString("%1/%2").arg(cueFileInfo.canonicalPath(), cueFileInfo.completeBaseName());
+ for(int n = 2; QDir(m_outputDir).exists(); n++)
+ {
+ m_outputDir = QString("%1/%2 (%3)").arg(cueFileInfo.canonicalPath(), cueFileInfo.completeBaseName(), QString::number(n));
+ }
+
setWindowTitle(QString("%1: %2").arg(windowTitle().split(":", QString::SkipEmptyParts).first().trimmed(), cueFileInfo.fileName()));
int iResult = m_model->loadCueSheet(m_cueFileName, QApplication::instance());
progress->show(tr("Analyzing file(s), please wait..."), analyzer);
progress->close();
- if(analyzer->filesAccepted() < files.count())
+ if(analyzer->filesAccepted() < static_cast<unsigned int>(files.count()))
{
if(QMessageBox::warning(this, tr("Analysis Failed"), tr("Warning: The format of some of the input files could not be determined!"), tr("Continue Anyway"), tr("Abort")) == 1)
{
void CueImportDialog::splitFiles(void)
{
- int nTracksSkipped = 0;
+ QString baseName = QFileInfo(m_cueFileName).completeBaseName().replace(".", " ").left(42).trimmed();
WorkingBanner *progress = new WorkingBanner(this);
- CueSplitter *splitter = new CueSplitter(m_outputDir, QFileInfo(m_cueFileName).completeBaseName().replace(".", " ").left(42).trimmed(), m_fileInfo);
- splitter->setAlbumInfo(m_model->getAlbumPerformer(), m_model->getAlbumTitle());
+ CueSplitter *splitter = new CueSplitter(m_outputDir, baseName, m_model, m_fileInfo);
connect(splitter, SIGNAL(fileSelected(QString)), progress, SLOT(setText(QString)), Qt::QueuedConnection);
+ connect(progress, SIGNAL(userAbort()), splitter, SLOT(abortProcess()), Qt::DirectConnection);
connect(splitter, SIGNAL(fileSplit(AudioFileModel)), m_fileList, SLOT(addFile(AudioFileModel)), Qt::QueuedConnection);
- int nFiles = m_model->getFileCount();
- for(int i = 0; i < nFiles; i++)
- {
- QString currentFileName = m_model->getFileName(i);
- int nTracks = m_model->getTrackCount(i);
-
- for(int j = 0; j < nTracks; j++)
- {
- int trackNo = m_model->getTrackNo(i, j);
- double startIndex = std::numeric_limits<double>::quiet_NaN();
- double duration = std::numeric_limits<double>::quiet_NaN();
- m_model->getTrackIndex(i, j, &startIndex, &duration);
-
- AudioFileModel metaInfo(QString().sprintf("cue://File%02d/Track%02d", i, j));
- metaInfo.setFileName(m_model->getTrackTitle(i, j));
- metaInfo.setFileArtist(m_model->getTrackPerformer(i, j));
- metaInfo.setFilePosition(trackNo);
-
- try
- {
- splitter->addTrack(trackNo, currentFileName, startIndex, duration, metaInfo);
- }
- catch(char *err)
- {
- qWarning("Failed to add track #%02d: %s", trackNo, err);
- nTracksSkipped++;
- }
- }
- }
-
progress->show(tr("Splitting file(s), please wait..."), splitter);
progress->close();
- if(!splitter->getSuccess())
+ if(splitter->getAborted())
+ {
+ QMessageBox::warning(this, tr("Cue Sheet Error"), tr("Process was aborted by the user after %1 track(s)!").arg(QString::number(splitter->getTracksSuccess())));
+ }
+ else if(!splitter->getSuccess())
{
QMessageBox::warning(this, tr("Cue Sheet Error"), tr("An unexpected error has occured while splitting the Cue Sheet!"));
}
else
{
- QString text = QString("<nobr>%1</nobr>").arg(tr("Imported %1 track(s) from the Cue Sheet and skipped %2 track(s).").arg(QString::number(splitter->getTracksSuccess()), QString::number(splitter->getTracksSkipped() + nTracksSkipped)));
+ QString text = QString("<nobr>%1</nobr>").arg(tr("Imported %1 track(s) from the Cue Sheet and skipped %2 track(s).").arg(QString::number(splitter->getTracksSuccess()), QString::number(splitter->getTracksSkipped() /*+ nTracksSkipped*/)));
QMessageBox::information(this, tr("Cue Sheet Completed"), text);
}
void WorkingBanner::keyPressEvent(QKeyEvent *event)
{
+ if(event->key() == Qt::Key_Escape)
+ {
+ emit userAbort();
+ }
+
event->ignore();
}
public slots:
void setText(const QString &text);
+signals:
+ void userAbort(void);
+
protected:
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
// Constructor & Destructor
////////////////////////////////////////////////////////////
+QMutex CueSheetModel::m_mutex(QMutex::Recursive);
+
CueSheetModel::CueSheetModel()
:
m_fileIcon(":/icons/music.png"),
QModelIndex CueSheetModel::index(int row, int column, const QModelIndex &parent) const
{
+ QMutexLocker lock(&m_mutex);
+
if(!parent.isValid())
{
return (row < m_files.count()) ? createIndex(row, column, m_files.at(row)) : QModelIndex();
int CueSheetModel::columnCount(const QModelIndex &parent) const
{
+ QMutexLocker lock(&m_mutex);
return 4;
}
int CueSheetModel::rowCount(const QModelIndex &parent) const
{
+ QMutexLocker lock(&m_mutex);
+
if(!parent.isValid())
{
return m_files.count();
QModelIndex CueSheetModel::parent(const QModelIndex &child) const
{
+ QMutexLocker lock(&m_mutex);
+
if(child.isValid())
{
CueSheetItem *childItem = static_cast<CueSheetItem*>(child.internalPointer());
QVariant CueSheetModel::headerData (int section, Qt::Orientation orientation, int role) const
{
+ QMutexLocker lock(&m_mutex);
+
if(role == Qt::DisplayRole)
{
switch(section)
QVariant CueSheetModel::data(const QModelIndex &index, int role) const
{
+ QMutexLocker lock(&m_mutex);
+
if(role == Qt::DisplayRole)
{
CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
void CueSheetModel::clearData(void)
{
+ QMutexLocker lock(&m_mutex);
+
beginResetModel();
while(!m_files.isEmpty()) delete m_files.takeLast();
endResetModel();
int CueSheetModel::getFileCount(void)
{
+ QMutexLocker lock(&m_mutex);
return m_files.count();
}
QString CueSheetModel::getFileName(int fileIndex)
{
+ QMutexLocker lock(&m_mutex);
+
if(fileIndex < 0 || fileIndex >= m_files.count())
{
return QString();
int CueSheetModel::getTrackCount(int fileIndex)
{
+ QMutexLocker lock(&m_mutex);
+
if(fileIndex < 0 || fileIndex >= m_files.count())
{
return -1;
int CueSheetModel::getTrackNo(int fileIndex, int trackIndex)
{
+ QMutexLocker lock(&m_mutex);
+
if(fileIndex >= 0 && fileIndex < m_files.count())
{
CueSheetFile *currentFile = m_files.at(fileIndex);
void CueSheetModel::getTrackIndex(int fileIndex, int trackIndex, double *startIndex, double *duration)
{
+ QMutexLocker lock(&m_mutex);
+
*startIndex = std::numeric_limits<double>::quiet_NaN();
*duration = std::numeric_limits<double>::quiet_NaN();
QString CueSheetModel::getTrackPerformer(int fileIndex, int trackIndex)
{
+ QMutexLocker lock(&m_mutex);
+
if(fileIndex >= 0 && fileIndex < m_files.count())
{
CueSheetFile *currentFile = m_files.at(fileIndex);
return currentTrack->performer();
}
}
+
return QString();
}
QString CueSheetModel::getTrackTitle(int fileIndex, int trackIndex)
{
+ QMutexLocker lock(&m_mutex);
+
if(fileIndex >= 0 && fileIndex < m_files.count())
{
CueSheetFile *currentFile = m_files.at(fileIndex);
return currentTrack->title();
}
}
+
return QString();
}
+QString CueSheetModel::getAlbumPerformer(void)
+{
+ QMutexLocker lock(&m_mutex);
+ return m_albumPerformer;
+}
+
+QString CueSheetModel::getAlbumTitle(void)
+{
+ QMutexLocker lock(&m_mutex);
+ return m_albumTitle;
+}
+
////////////////////////////////////////////////////////////
// Cue Sheet Parser
////////////////////////////////////////////////////////////
int CueSheetModel::loadCueSheet(const QString &cueFileName, QCoreApplication *application)
{
+ QMutexLocker lock(&m_mutex);
+
QFile cueFile(cueFileName);
if(!cueFile.open(QIODevice::ReadOnly))
{
void getTrackIndex(int fileIndex, int trackIndex, double *startIndex, double *duration);
QString getTrackPerformer(int fileIndex, int trackIndex);
QString getTrackTitle(int fileIndex, int trackIndex);
- QString getAlbumPerformer(void) { return m_albumPerformer; }
- QString getAlbumTitle(void) { return m_albumTitle; }
+ QString getAlbumPerformer(void);
+ QString getAlbumTitle(void);
//Cue Sheet functions
int loadCueSheet(const QString &cueFile, QCoreApplication *application = NULL);
double parseTimeIndex(const QString &index);
QString indexToString(const double index) const;
+ static QMutex m_mutex;
+
QList<CueSheetFile*> m_files;
QString m_albumTitle;
QString m_albumPerformer;
#include "Global.h"
#include "LockedFile.h"
#include "Model_AudioFile.h"
-#include "PlaylistImporter.h"
+#include "Model_CueSheet.h"
#include "Registry_Decoder.h"
#include "Decoder_Abstract.h"
// Constructor
////////////////////////////////////////////////////////////
-CueSplitter::CueSplitter(const QString &outputDir, const QString &baseName, const QList<AudioFileModel> &inputFiles)
+CueSplitter::CueSplitter(const QString &outputDir, const QString &baseName, CueSheetModel *model, const QList<AudioFileModel> &inputFilesInfo)
:
+ m_model(model),
m_outputDir(outputDir),
m_baseName(baseName),
m_soxBin(lamexp_lookup_tool("sox.exe"))
qFatal("Invalid path to SoX binary. Tool not initialized properly.");
}
- m_albumPerformer.clear();
- m_albumTitle.clear();
m_decompressedFiles.clear();
m_tempFiles.clear();
qDebug("\n[CueSplitter::CueSplitter]");
- int nInputFiles = inputFiles.count();
+ int nInputFiles = inputFilesInfo.count();
for(int i = 0; i < nInputFiles; i++)
{
- m_inputFiles.insert(inputFiles[i].filePath(), inputFiles[i]);
- qDebug("%02d <%s>", i, inputFiles[i].filePath().toUtf8().constData());
+ m_inputFilesInfo.insert(inputFilesInfo[i].filePath(), inputFilesInfo[i]);
+ qDebug("%02d <%s>", i, inputFilesInfo[i].filePath().toUtf8().constData());
}
qDebug("All input files added.");
void CueSplitter::run()
{
m_bSuccess = false;
+ m_bAborted = false;
m_abortFlag = false;
m_nTracksSuccess = 0;
m_nTracksSkipped = 0;
return;
}
- QStringList inputFileList = m_inputFiles.keys();
+ QStringList inputFileList = m_inputFilesInfo.keys();
int nInputFiles = inputFileList.count();
//Decompress all input files
for(int i = 0; i < nInputFiles; i++)
{
- AudioFileModel &inputFileInfo = m_inputFiles[inputFileList.at(i)];
+ AudioFileModel &inputFileInfo = m_inputFilesInfo[inputFileList.at(i)];
if(inputFileInfo.formatContainerType().compare("Wave", Qt::CaseInsensitive) || inputFileInfo.formatAudioType().compare("PCM", Qt::CaseInsensitive))
{
AbstractDecoder *decoder = DecoderRegistry::lookup(inputFileInfo.formatContainerType(), inputFileInfo.formatContainerProfile(), inputFileInfo.formatAudioType(), inputFileInfo.formatAudioProfile(), inputFileInfo.formatAudioVersion());
{
m_decompressedFiles.insert(inputFileList.at(i), inputFileList.at(i));
}
+ if(m_abortFlag)
+ {
+ m_bAborted = true;
+ qWarning("The user has requested to abort the process!");
+ return;
+ }
}
- int nTracks = min(min(min(m_trackFile.count(), m_trackNo.count()), min(m_trackOffset.count(), m_trackLength.count())), m_trackMetaInfo.count());
+ int nFiles = m_model->getFileCount();
+ QString albumPerformer = m_model->getAlbumPerformer();
+ QString albumTitle = m_model->getAlbumTitle();
//Now split all tracks
- for(int i = 0; i < nTracks; i++)
+ for(int i = 0; i < nFiles; i++)
{
- QString outputFile = QString("%1/%2 - Track %3.wav").arg(m_outputDir, m_baseName, QString().sprintf("%02d", m_trackNo.at(i)));
- for(int n = 2; QFileInfo(outputFile).exists(); n++)
+ int nTracks = m_model->getTrackCount(i);
+ QString trackFile = m_model->getFileName(i);
+ int maxProgress = 0;
+
+ for(int j = 0; j < nTracks; j++)
{
- outputFile = QString("%1/%2 - Track %3 (%4).wav").arg(m_outputDir, m_baseName, QString().sprintf("%02d", m_trackNo.at(i)), QString::number(n));
- }
+ int trackNo = m_model->getTrackNo(i, j);
+ double trackOffset = std::numeric_limits<double>::quiet_NaN();
+ double trackLength = std::numeric_limits<double>::quiet_NaN();
+ m_model->getTrackIndex(i, j, &trackOffset, &trackLength);
+
+ if((trackNo < 0) || _isnan(trackOffset) || _isnan(trackLength))
+ {
+ qWarning("Failed to fetch information for track #%d of file #%d!", j, i);
+ continue;
+ }
+
+ AudioFileModel trackMetaInfo(QString().sprintf("cue://File%02d/Track%02d", i, j));
+ trackMetaInfo.setFileName(m_model->getTrackTitle(i, j));
+ trackMetaInfo.setFileArtist(m_model->getTrackPerformer(i, j));
+ trackMetaInfo.setFilePosition(trackNo);
- emit fileSelected(QFileInfo(outputFile).fileName());
- splitFile(outputFile, m_trackNo.at(i), m_trackFile.at(i), m_trackOffset.at(i), m_trackLength.at(i), m_trackMetaInfo.at(i));
- }
+ if(!albumTitle.isEmpty())
+ {
+ trackMetaInfo.setFileAlbum(albumTitle);
+ }
+ if(!albumPerformer.isEmpty() && trackMetaInfo.fileArtist().isEmpty())
+ {
+ trackMetaInfo.setFileArtist(albumPerformer);
+ }
+ if(_finite(trackLength))
+ {
+ trackMetaInfo.setFileDuration(static_cast<unsigned int>(abs(trackLength)));
+ }
- qDebug("All files were split.\n");
- m_bSuccess = true;
-}
+ QString outputFile = QString("%1/%2 - Track %3.wav").arg(m_outputDir, m_baseName, QString().sprintf("%02d", trackNo));
+ for(int n = 2; QFileInfo(outputFile).exists(); n++)
+ {
+ outputFile = QString("%1/%2 - Track %3 (%4).wav").arg(m_outputDir, m_baseName, QString().sprintf("%02d", trackNo), QString::number(n));
+ }
-void CueSplitter::addTrack(const int trackNo, const QString &file, const double offset, const double length, const AudioFileModel &metaInfo)
-{
-
- if(m_inputFiles.contains(file))
- {
- m_trackFile << file;
- m_trackNo << trackNo;
- m_trackOffset << offset;
- m_trackLength << length;
- m_trackMetaInfo << metaInfo;
- }
- else
- {
- throw "The input file is unknown/missing!";
+ emit fileSelected(QFileInfo(outputFile).fileName());
+ splitFile(outputFile, trackNo, trackFile, trackOffset, trackLength, trackMetaInfo, maxProgress);
+
+ if(m_abortFlag)
+ {
+ m_bAborted = true;
+ qWarning("The user has requested to abort the process!");
+ return;
+ }
+ }
}
-}
-void CueSplitter::setAlbumInfo(const QString &performer, const QString &title)
-{
- if(!performer.isEmpty()) m_albumPerformer = performer;
- if(!title.isEmpty()) m_albumTitle = title;
+ qDebug("All files were split.\n");
+ m_bSuccess = true;
}
////////////////////////////////////////////////////////////
// Privtae Functions
////////////////////////////////////////////////////////////
-void CueSplitter::splitFile(const QString &output, const int trackNo, const QString &file, const double offset, const double length, const AudioFileModel &metaInfo)
+void CueSplitter::splitFile(const QString &output, const int trackNo, const QString &file, const double offset, const double length, const AudioFileModel &metaInfo, int &maxProgress)
{
qDebug("[Track %02d]", trackNo);
qDebug("File: <%s>", file.toUtf8().constData());
QString decompressedInput = m_decompressedFiles[file];
qDebug("Input: <%s>", decompressedInput.toUtf8().constData());
+ emit fileSelected(QString("%1 [%2%]").arg(baseName, QString::number(maxProgress)));
+
AudioFileModel outFileInfo(metaInfo);
outFileInfo.setFilePath(output);
outFileInfo.setFormatContainerType("Wave");
outFileInfo.setFormatAudioType("PCM");
-
- if(length != std::numeric_limits<double>::infinity())
- {
- outFileInfo.setFileDuration(static_cast<unsigned int>(abs(length)));
- }
- if(!m_albumTitle.isEmpty())
- {
- outFileInfo.setFileAlbum(m_albumTitle);
- }
- if(!m_albumPerformer.isEmpty() && outFileInfo.fileArtist().isEmpty())
- {
- outFileInfo.setFileArtist(m_albumPerformer);
- }
QStringList args;
args << "-S" << "-V3";
args << QDir::toNativeSeparators(output);
//Add trim parameters, if needed
- if(offset != 0.0 || length != std::numeric_limits<double>::infinity())
+ if(_finite(offset) || _finite(length))
{
args << "trim";
args << indexToString(offset);
- if((length != std::numeric_limits<double>::quiet_NaN()) && (length != std::numeric_limits<double>::infinity()))
+ if(_finite(length))
{
args << indexToString(length);
}
if(m_abortFlag)
{
process.kill();
+ qWarning("Process was aborted on user request!");
break;
}
process.waitForReadyRead();
int progress = rxProgress.cap(1).toInt(&ok);
if(ok)
{
- emit fileSelected(QString("%1 [%2%]").arg(baseName, QString::number(progress)));
+ maxProgress = max(maxProgress, progress);
+ emit fileSelected(QString("%1 [%2%]").arg(baseName, QString::number(maxProgress)));
}
}
else if(rxChannels.lastIndexIn(text) >= 0)
QString CueSplitter::indexToString(const double index) const
{
- if(index == std::numeric_limits<double>::quiet_NaN() || index == std::numeric_limits<double>::infinity() || index < 0.0)
+ if(!_finite(index) || (index < 0.0))
{
return QString();
}
#include <QMap>
class AudioFileModel;
+class CueSheetModel;
class QFile;
class QDir;
class QFileInfo;
Q_OBJECT
public:
- CueSplitter(const QString &outputDir, const QString &baseName, const QList<AudioFileModel> &inputFiles);
+ CueSplitter(const QString &outputDir, const QString &baseName, CueSheetModel *model, const QList<AudioFileModel> &inputFilesInfo);
~CueSplitter(void);
+
void run();
- bool getSuccess(void) { return !isRunning() && m_bSuccess; }
unsigned int getTracksSuccess(void) { return m_nTracksSuccess; }
unsigned int getTracksSkipped(void) { return m_nTracksSkipped; }
- void addTrack(const int trackNo, const QString &file, const double offset, const double length, const AudioFileModel &metaInfo);
- void setAlbumInfo(const QString &performer, const QString &title);
+ bool getSuccess(void) { return !isRunning() && m_bSuccess; }
+ bool getAborted(void) { return m_bAborted; }
signals:
void fileSelected(const QString &fileName);
private slots:
void handleUpdate(int progress);
+public slots:
+ void abortProcess(void) { m_abortFlag = true; }
+
private:
- void splitFile(const QString &output, const int trackNo, const QString &file, const double offset, const double length, const AudioFileModel &metaInfo);
+ void splitFile(const QString &output, const int trackNo, const QString &file, const double offset, const double length, const AudioFileModel &metaInfo, int &maxProgress);
QString indexToString(const double index) const;
const QString m_soxBin;
const QString m_baseName;
unsigned int m_nTracksSuccess;
unsigned int m_nTracksSkipped;
+
+ bool m_bAborted;
bool m_bSuccess;
+
volatile bool m_abortFlag;
- QString m_albumTitle;
- QString m_albumPerformer;
- QString m_activeFile;
- QMap<QString,AudioFileModel> m_inputFiles;
+ CueSheetModel *m_model;
+ QMap<QString,AudioFileModel> m_inputFilesInfo;
QMap<QString,QString> m_decompressedFiles;
QStringList m_tempFiles;
- QList<QString> m_trackFile;
- QList<int> m_trackNo;
- QList<double> m_trackOffset;
- QList<double> m_trackLength;
- QList<AudioFileModel> m_trackMetaInfo;
+ QString m_activeFile;
};