///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
-// Copyright (C) 2004-2011 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
//
// 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
#include "Genres.h"
#include <QApplication>
-#include <QMessageBox>
-#include <QInputDialog>
+#include <QDir>
#include <QFileInfo>
+#include <QFont>
+#include <QTime>
+#include <QTextCodec>
+#include <QTextStream>
#include <float.h>
#include <limits>
+#define UNQUOTE(STR) STR.split("\"", QString::SkipEmptyParts).first().trimmed()
+
////////////////////////////////////////////////////////////
// Helper Classes
////////////////////////////////////////////////////////////
{
public:
virtual const char* type(void) = 0;
+ virtual bool isValid(void) { return false; }
};
class CueSheetTrack : public CueSheetItem
m_trackNo(trackNo)
{
m_startIndex = std::numeric_limits<double>::quiet_NaN();
+ m_duration = std::numeric_limits<double>::infinity();
+ m_year = 0;
}
int trackNo(void) { return m_trackNo; }
double startIndex(void) { return m_startIndex; }
+ double duration(void) { return m_duration; }
QString title(void) { return m_title; }
QString performer(void) { return m_performer; }
+ QString genre(void) { return m_genre; }
+ unsigned int year(void) { return m_year; }
CueSheetFile *parent(void) { return m_parent; }
void setStartIndex(double startIndex) { m_startIndex = startIndex; }
- void setTitle(const QString &title) { m_title = title; }
- void setPerformer(const QString &performer) { m_performer = performer; }
- bool isValid(void) { return !(_isnan(m_startIndex) || (m_trackNo < 0)); }
+ void setDuration(double duration) { m_duration = duration; }
+ void setTitle(const QString &title, bool update = false) { if(!update || (m_title.isEmpty() && !title.isEmpty())) m_title = title; }
+ void setPerformer(const QString &performer, bool update = false) { if(!update || (m_performer.isEmpty() && !performer.isEmpty())) m_performer = performer; }
+ void setGenre(const QString &genre, bool update = false) { if(!update || (m_genre.isEmpty() && !m_genre.isEmpty())) m_genre = genre; }
+ void setYear(const unsigned int year, bool update = false) { if(!update || (year == 0)) m_year = year; }
+ virtual bool isValid(void) { return !(_isnan(m_startIndex) || (m_trackNo < 0)); }
virtual const char* type(void) { return "CueSheetTrack"; }
private:
int m_trackNo;
double m_startIndex;
+ double m_duration;
QString m_title;
QString m_performer;
+ QString m_genre;
+ unsigned int m_year;
CueSheetFile *m_parent;
};
void clearTracks(void) { while(!m_tracks.isEmpty()) delete m_tracks.takeLast(); }
CueSheetTrack *track(int index) { return m_tracks.at(index); }
int trackCount(void) { return m_tracks.count(); }
+ virtual bool isValid(void) { return m_tracks.count() > 0; }
virtual const char* type(void) { return "CueSheetFile"; }
private:
const QString m_fileName;
// Constructor & Destructor
////////////////////////////////////////////////////////////
+QMutex CueSheetModel::m_mutex(QMutex::Recursive);
+
CueSheetModel::CueSheetModel()
+:
+ m_fileIcon(":/icons/music.png"),
+ m_trackIcon(":/icons/control_play_blue.png")
{
int trackNo = 0;
+ m_albumYear = 0;
for(int i = 0; i < 5; i++)
{
QModelIndex CueSheetModel::index(int row, int column, const QModelIndex &parent) const
{
+ QMutexLocker lock(&m_mutex);
+
if(!parent.isValid())
{
- return createIndex(row, column, m_files.at(row));
+ return (row < m_files.count()) ? createIndex(row, column, m_files.at(row)) : QModelIndex();
}
CueSheetItem *parentItem = static_cast<CueSheetItem*>(parent.internalPointer());
if(CueSheetFile *filePtr = dynamic_cast<CueSheetFile*>(parentItem))
{
- return createIndex(row, column, filePtr->track(row));
+ return (row < filePtr->trackCount()) ? createIndex(row, column, filePtr->track(row)) : QModelIndex();
}
return QModelIndex();
int CueSheetModel::columnCount(const QModelIndex &parent) const
{
- return 3;
+ 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)
case 2:
return tr("Index");
break;
+ case 3:
+ return tr("Duration");
+ break;
default:
return QVariant();
break;
QVariant CueSheetModel::data(const QModelIndex &index, int role) const
{
+ QMutexLocker lock(&m_mutex);
+
if(role == Qt::DisplayRole)
{
CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
return tr("File %1").arg(QString().sprintf("%02d", index.row() + 1)).append(" ");
break;
case 1:
- return QString("[%1]").arg(QFileInfo(filePtr->fileName()).fileName());
+ return QFileInfo(filePtr->fileName()).fileName();
break;
default:
return QVariant();
case 2:
return indexToString(trackPtr->startIndex());
break;
+ case 3:
+ return indexToString(trackPtr->duration());
+ break;
default:
return QVariant();
break;
}
}
}
+ else if(role == Qt::ToolTipRole)
+ {
+ CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
+
+ if(CueSheetFile *filePtr = dynamic_cast<CueSheetFile*>(item))
+ {
+ return QDir::toNativeSeparators(filePtr->fileName());
+ }
+ else if(CueSheetTrack *trackPtr = dynamic_cast<CueSheetTrack*>(item))
+ {
+ return QDir::toNativeSeparators(trackPtr->parent()->fileName());
+ }
+ }
+ else if(role == Qt::DecorationRole)
+ {
+ if(index.column() == 0)
+ {
+ CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
+
+ if(dynamic_cast<CueSheetFile*>(item))
+ {
+ return m_fileIcon;
+ }
+ else if(dynamic_cast<CueSheetTrack*>(item))
+ {
+ return m_trackIcon;
+ }
+ }
+ }
+ else if(role == Qt::FontRole)
+ {
+ QFont font("Monospace");
+ font.setStyleHint(QFont::TypeWriter);
+ if((index.column() == 1))
+ {
+ CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
+ font.setBold(dynamic_cast<CueSheetFile*>(item) != NULL);
+ }
+ return font;
+ }
+ else if(role == Qt::ForegroundRole)
+ {
+ if((index.column() == 1))
+ {
+ CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
+ if(CueSheetFile *filePtr = dynamic_cast<CueSheetFile*>(item))
+ {
+ return (QFileInfo(filePtr->fileName()).size() > 4) ? QColor("mediumblue") : QColor("darkred");
+ }
+ }
+ else if((index.column() == 3))
+ {
+ CueSheetItem *item = reinterpret_cast<CueSheetItem*>(index.internalPointer());
+ if(CueSheetTrack *trackPtr = dynamic_cast<CueSheetTrack*>(item))
+ {
+ if(trackPtr->duration() == std::numeric_limits<double>::infinity())
+ {
+ return QColor("dimgrey");
+ }
+ }
+ }
+ }
return QVariant();
}
void CueSheetModel::clearData(void)
{
+ QMutexLocker lock(&m_mutex);
+
beginResetModel();
while(!m_files.isEmpty()) delete m_files.takeLast();
endResetModel();
}
////////////////////////////////////////////////////////////
+// External API
+////////////////////////////////////////////////////////////
+
+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();
+ }
+
+ return m_files.at(fileIndex)->fileName();
+}
+
+int CueSheetModel::getTrackCount(int fileIndex)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if(fileIndex < 0 || fileIndex >= m_files.count())
+ {
+ return -1;
+ }
+
+ return m_files.at(fileIndex)->trackCount();
+}
+
+int CueSheetModel::getTrackNo(int fileIndex, int trackIndex)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if(fileIndex >= 0 && fileIndex < m_files.count())
+ {
+ CueSheetFile *currentFile = m_files.at(fileIndex);
+ if(trackIndex >= 0 && trackIndex < currentFile->trackCount())
+ {
+ return currentFile->track(trackIndex)->trackNo();
+ }
+ }
+
+ return -1;
+}
+
+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();
+
+ if(fileIndex >= 0 && fileIndex < m_files.count())
+ {
+ CueSheetFile *currentFile = m_files.at(fileIndex);
+ if(trackIndex >= 0 && trackIndex < currentFile->trackCount())
+ {
+ CueSheetTrack *currentTrack = currentFile->track(trackIndex);
+ *startIndex = currentTrack->startIndex();
+ *duration = currentTrack->duration();
+ }
+ }
+}
+
+QString CueSheetModel::getTrackPerformer(int fileIndex, int trackIndex)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if(fileIndex >= 0 && fileIndex < m_files.count())
+ {
+ CueSheetFile *currentFile = m_files.at(fileIndex);
+ if(trackIndex >= 0 && trackIndex < currentFile->trackCount())
+ {
+ CueSheetTrack *currentTrack = currentFile->track(trackIndex);
+ 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);
+ if(trackIndex >= 0 && trackIndex < currentFile->trackCount())
+ {
+ CueSheetTrack *currentTrack = currentFile->track(trackIndex);
+ return currentTrack->title();
+ }
+ }
+
+ return QString();
+}
+
+QString CueSheetModel::getTrackGenre(int fileIndex, int trackIndex)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if(fileIndex >= 0 && fileIndex < m_files.count())
+ {
+ CueSheetFile *currentFile = m_files.at(fileIndex);
+ if(trackIndex >= 0 && trackIndex < currentFile->trackCount())
+ {
+ CueSheetTrack *currentTrack = currentFile->track(trackIndex);
+ return currentTrack->genre();
+ }
+ }
+
+ return QString();
+}
+
+unsigned int CueSheetModel::getTrackYear(int fileIndex, int trackIndex)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if(fileIndex >= 0 && fileIndex < m_files.count())
+ {
+ CueSheetFile *currentFile = m_files.at(fileIndex);
+ if(trackIndex >= 0 && trackIndex < currentFile->trackCount())
+ {
+ CueSheetTrack *currentTrack = currentFile->track(trackIndex);
+ return currentTrack->year();
+ }
+ }
+
+ return 0;
+}
+
+QString CueSheetModel::getAlbumPerformer(void)
+{
+ QMutexLocker lock(&m_mutex);
+ return m_albumPerformer;
+}
+
+QString CueSheetModel::getAlbumTitle(void)
+{
+ QMutexLocker lock(&m_mutex);
+ return m_albumTitle;
+}
+
+QString CueSheetModel::getAlbumGenre(void)
+{
+ QMutexLocker lock(&m_mutex);
+ return m_albumGenre;
+}
+
+unsigned int CueSheetModel::getAlbumYear(void)
+{
+ QMutexLocker lock(&m_mutex);
+ return m_albumYear;
+}
+////////////////////////////////////////////////////////////
// Cue Sheet Parser
////////////////////////////////////////////////////////////
-int CueSheetModel::loadCueSheet(const QString &cueFileName, QCoreApplication *application)
+int CueSheetModel::loadCueSheet(const QString &cueFileName, QCoreApplication *application, QTextCodec *forceCodec)
{
+ QMutexLocker lock(&m_mutex);
+ const QTextCodec *codec = (forceCodec != NULL) ? forceCodec : QTextCodec::codecForName("System");
+
QFile cueFile(cueFileName);
if(!cueFile.open(QIODevice::ReadOnly))
{
- return 1;
+ return ErrorIOFailure;
}
clearData();
beginResetModel();
- int iResult = parseCueFile(cueFile, application);
+ int iResult = parseCueFile(cueFile, QDir(QFileInfo(cueFile).canonicalPath()), application, codec);
endResetModel();
return iResult;
}
-int CueSheetModel::parseCueFile(QFile &cueFile, QCoreApplication *application)
+int CueSheetModel::parseCueFile(QFile &cueFile, const QDir &baseDir, QCoreApplication *application, const QTextCodec *codec)
{
- cueFile.seek(0);
+ cueFile.reset();
qDebug("\n[Cue Sheet Import]");
+ bool bForceLatin1 = false;
//Reject very large files, as parsing might take until forever
if(cueFile.size() >= 10485760i64)
return 2;
}
- //Check for UTF-8 BOM in order to guess encoding
- QByteArray bomCheck = cueFile.peek(128);
- bool bUTF8 = bomCheck.contains("\xef\xbb\xbf");
- qDebug("Encoding is %s.", (bUTF8 ? "UTF-8" : "Local 8-Bit"));
- bomCheck.clear();
+ //Test selected Codepage for decoding errors
+ qDebug("Character encoding is: %s.", codec->name().constData());
+ const QString replacementSymbol = QString(QChar(QChar::ReplacementCharacter));
+ QByteArray testData = cueFile.peek(1048576);
+ if((!testData.isEmpty()) && codec->toUnicode(testData.constData(), testData.size()).contains(replacementSymbol))
+ {
+ qWarning("Decoding error using selected codepage (%s). Enforcing Latin-1.", codec->name().constData());
+ bForceLatin1 = true;
+ }
+ testData.clear();
+
+ //Init text stream
+ QTextStream cueStream(&cueFile);
+ cueStream.setAutoDetectUnicode(false);
+ cueStream.setCodec(bForceLatin1 ? "latin1" : codec->name());
+ cueStream.seek(0i64);
- QRegExp rxFile("^FILE\\s+\"([^\"]+)\"\\s+(\\w+)$", Qt::CaseInsensitive);
+ //Create regular expressions
+ QRegExp rxFile("^FILE\\s+(\"[^\"]+\"|\\S+)\\s+(\\w+)$", Qt::CaseInsensitive);
QRegExp rxTrack("^TRACK\\s+(\\d+)\\s(\\w+)$", Qt::CaseInsensitive);
QRegExp rxIndex("^INDEX\\s+(\\d+)\\s+([0-9:]+)$", Qt::CaseInsensitive);
- QRegExp rxTitle("^TITLE\\s+\"([^\"]+)\"$", Qt::CaseInsensitive);
- QRegExp rxPerformer("^PERFORMER\\s+\"([^\"]+)\"$", Qt::CaseInsensitive);
+ QRegExp rxTitle("^TITLE\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive);
+ QRegExp rxPerformer("^PERFORMER\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive);
+ QRegExp rxGenre("^REM\\s+GENRE\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive);
+ QRegExp rxYear("^REM\\s+DATE\\s+(\\d+)$", Qt::CaseInsensitive);
bool bPreamble = true;
bool bUnsupportedTrack = false;
CueSheetFile *currentFile = NULL;
CueSheetTrack *currentTrack = NULL;
- QString albumTitle;
- QString albumPerformer;
+ m_albumTitle.clear();
+ m_albumPerformer.clear();
+ m_albumGenre.clear();
+ m_albumYear = 0;
//Loop over the Cue Sheet until all lines were processed
- while(true)
+ for(int lines = 0; lines < INT_MAX; lines++)
{
if(application)
{
application->processEvents();
- Sleep(25);
+ if(lines < 128) Sleep(10);
}
- QByteArray lineData = cueFile.readLine();
- if(lineData.size() <= 0)
+ if(cueStream.atEnd())
{
qDebug("End of Cue Sheet file.");
break;
}
- QString line = bUTF8 ? QString::fromUtf8(lineData.constData(), lineData.size()).trimmed() : QString::fromLocal8Bit(lineData.constData(), lineData.size()).trimmed();
+ QString line = cueStream.readLine().trimmed();
/* --- FILE --- */
if(rxFile.indexIn(line) >= 0)
{
- qDebug("File: <%s> <%s>", rxFile.cap(1).toUtf8().constData(), rxFile.cap(2).toUtf8().constData());
+ qDebug("%03d File: <%s> <%s>", lines, rxFile.cap(1).toUtf8().constData(), rxFile.cap(2).toUtf8().constData());
if(currentFile)
{
if(currentTrack)
{
if(currentTrack->isValid())
{
- if(currentTrack->title().isEmpty() && !albumTitle.isEmpty())
- {
- currentTrack->setTitle(albumTitle);
- }
- if(currentTrack->performer().isEmpty() && !albumPerformer.isEmpty())
- {
- currentTrack->setPerformer(albumPerformer);
- }
currentFile->addTrack(currentTrack);
currentTrack = NULL;
}
LAMEXP_DELETE(currentTrack);
}
}
- if(currentFile->trackCount() > 0)
+ if(currentFile->isValid())
{
m_files.append(currentFile);
currentFile = NULL;
}
if(!rxFile.cap(2).compare("WAVE", Qt::CaseInsensitive) || !rxFile.cap(2).compare("MP3", Qt::CaseInsensitive) || !rxFile.cap(2).compare("AIFF", Qt::CaseInsensitive))
{
- currentFile = new CueSheetFile(rxFile.cap(1));
+ currentFile = new CueSheetFile(baseDir.absoluteFilePath(UNQUOTE(rxFile.cap(1))));
+ qDebug("%03d File path: <%s>", lines, currentFile->fileName().toUtf8().constData());
}
else
{
bUnsupportedTrack = true;
- qWarning("Skipping unsupported file of type '%s'.", rxFile.cap(2).toUtf8().constData());
+ qWarning("%03d Skipping unsupported file of type '%s'.", lines, rxFile.cap(2).toUtf8().constData());
currentFile = NULL;
}
bPreamble = false;
{
if(currentFile)
{
- qDebug(" Track: <%s> <%s>", rxTrack.cap(1).toUtf8().constData(), rxTrack.cap(2).toUtf8().constData());
+ qDebug("%03d Track: <%s> <%s>", lines, rxTrack.cap(1).toUtf8().constData(), rxTrack.cap(2).toUtf8().constData());
if(currentTrack)
{
if(currentTrack->isValid())
{
- if(currentTrack->title().isEmpty() && !albumTitle.isEmpty())
- {
- currentTrack->setTitle(albumTitle);
- }
- if(currentTrack->performer().isEmpty() && !albumPerformer.isEmpty())
- {
- currentTrack->setPerformer(albumPerformer);
- }
currentFile->addTrack(currentTrack);
currentTrack = NULL;
}
else
{
bUnsupportedTrack = true;
- qWarning(" Skipping unsupported track of type '%s'.", rxTrack.cap(2).toUtf8().constData());
+ qWarning("%03d Skipping unsupported track of type '%s'.", lines, rxTrack.cap(2).toUtf8().constData());
currentTrack = NULL;
}
}
{
if(currentFile && currentTrack)
{
- qDebug(" Index: <%s> <%s>", rxIndex.cap(1).toUtf8().constData(), rxIndex.cap(2).toUtf8().constData());
+ qDebug("%03d Index: <%s> <%s>", lines, rxIndex.cap(1).toUtf8().constData(), rxIndex.cap(2).toUtf8().constData());
if(rxIndex.cap(1).toInt() == 1)
{
currentTrack->setStartIndex(parseTimeIndex(rxIndex.cap(2)));
{
if(bPreamble)
{
- albumTitle = rxTitle.cap(1);
+ m_albumTitle = UNQUOTE(rxTitle.cap(1)).simplified();
}
else if(currentFile && currentTrack)
{
- qDebug(" Title: <%s>", rxTitle.cap(1).toUtf8().constData());
- currentTrack->setTitle(rxTitle.cap(1));
+ qDebug("%03d Title: <%s>", lines, rxTitle.cap(1).toUtf8().constData());
+ currentTrack->setTitle(UNQUOTE(rxTitle.cap(1)).simplified());
}
continue;
}
{
if(bPreamble)
{
- albumPerformer = rxPerformer.cap(1);
+ m_albumPerformer = UNQUOTE(rxPerformer.cap(1)).simplified();
+ }
+ else if(currentFile && currentTrack)
+ {
+ qDebug("%03d Title: <%s>", lines, rxPerformer.cap(1).toUtf8().constData());
+ currentTrack->setPerformer(UNQUOTE(rxPerformer.cap(1)).simplified());
+ }
+ continue;
+ }
+
+ /* --- GENRE --- */
+ if(rxGenre.indexIn(line) >= 0)
+ {
+ if(bPreamble)
+ {
+ QString temp = UNQUOTE(rxGenre.cap(1)).simplified();
+ for(int i = 0; g_lamexp_generes[i]; i++)
+ {
+ if(temp.compare(g_lamexp_generes[i], Qt::CaseInsensitive) == 0)
+ {
+ m_albumGenre = QString(g_lamexp_generes[i]);
+ break;
+ }
+ }
+ }
+ else if(currentFile && currentTrack)
+ {
+ qDebug("%03d Genre: <%s>", lines, rxGenre.cap(1).toUtf8().constData());
+ QString temp = UNQUOTE(rxGenre.cap(1).simplified());
+ for(int i = 0; g_lamexp_generes[i]; i++)
+ {
+ if(temp.compare(g_lamexp_generes[i], Qt::CaseInsensitive) == 0)
+ {
+ currentTrack->setGenre(QString(g_lamexp_generes[i]));
+ break;
+ }
+ }
+ }
+ continue;
+ }
+
+ /* --- YEAR --- */
+ if(rxYear.indexIn(line) >= 0)
+ {
+ if(bPreamble)
+ {
+ bool ok = false;
+ unsigned int temp = rxYear.cap(1).toUInt(&ok);
+ if(ok) m_albumYear = temp;
}
else if(currentFile && currentTrack)
{
- qDebug(" Title: <%s>", rxPerformer.cap(1).toUtf8().constData());
- currentTrack->setPerformer(rxPerformer.cap(1));
+ qDebug("%03d Year: <%s>", lines, rxPerformer.cap(1).toUtf8().constData());
+ bool ok = false;
+ unsigned int temp = rxYear.cap(1).toUInt(&ok);
+ if(ok) currentTrack->setYear(temp);
}
continue;
}
}
- //Finally append the very last track/file
+ //Append the very last track/file that is still pending
if(currentFile)
{
if(currentTrack)
{
if(currentTrack->isValid())
{
- if(currentTrack->title().isEmpty() && !albumTitle.isEmpty())
- {
- currentTrack->setTitle(albumTitle);
- }
- if(currentTrack->performer().isEmpty() && !albumPerformer.isEmpty())
- {
- currentTrack->setPerformer(albumPerformer);
- }
currentFile->addTrack(currentTrack);
currentTrack = NULL;
}
LAMEXP_DELETE(currentTrack);
}
}
- if(currentFile->trackCount() > 0)
+ if(currentFile->isValid())
{
m_files.append(currentFile);
currentFile = NULL;
}
}
- return (m_files.count() > 0) ? 0 : (bUnsupportedTrack ? 3 : 2);
+ //Finally calculate duration of each track
+ int nFiles = m_files.count();
+ for(int i = 0; i < nFiles; i++)
+ {
+ if(application)
+ {
+ application->processEvents();
+ Sleep(10);
+ }
+
+ CueSheetFile *currentFile = m_files.at(i);
+ int nTracks = currentFile->trackCount();
+ if(nTracks > 1)
+ {
+ for(int j = 1; j < nTracks; j++)
+ {
+ CueSheetTrack *currentTrack = currentFile->track(j);
+ CueSheetTrack *previousTrack = currentFile->track(j-1);
+ double duration = currentTrack->startIndex() - previousTrack->startIndex();
+ previousTrack->setDuration(qMax(0.0, duration));
+ }
+ }
+ }
+
+ //Sanity check of track numbers
+ if(nFiles > 0)
+ {
+ bool hasTracks = false;
+ int previousTrackNo = -1;
+ bool trackNo[100];
+ for(int i = 0; i < 100; i++)
+ {
+ trackNo[i] = false;
+ }
+
+ for(int i = 0; i < nFiles; i++)
+ {
+ if(application)
+ {
+ application->processEvents();
+ Sleep(10);
+ }
+ CueSheetFile *currentFile = m_files.at(i);
+ int nTracks = currentFile->trackCount();
+ if(nTracks > 1)
+ {
+ for(int j = 0; j < nTracks; j++)
+ {
+ int currentTrackNo = currentFile->track(j)->trackNo();
+ if(currentTrackNo > 99)
+ {
+ qWarning("Track #%02d is invalid (maximum is 99), Cue Sheet is inconsistent!", currentTrackNo);
+ return ErrorInconsistent;
+ }
+ if(currentTrackNo <= previousTrackNo)
+ {
+ qWarning("Non-increasing track numbers (%02d -> %02d), Cue Sheet is inconsistent!", previousTrackNo, currentTrackNo);
+ return ErrorInconsistent;
+ }
+ if(trackNo[currentTrackNo])
+ {
+ qWarning("Track #%02d exists multiple times, Cue Sheet is inconsistent!", currentTrackNo);
+ return ErrorInconsistent;
+ }
+ trackNo[currentTrackNo] = true;
+ previousTrackNo = currentTrackNo;
+ hasTracks = true;
+ }
+ }
+ }
+
+ if(!hasTracks)
+ {
+ qWarning("Could not find at least one valid track in the Cue Sheet!");
+ return ErrorInconsistent;
+ }
+
+ return ErrorSuccess;
+ }
+ else
+ {
+ qWarning("Could not find at least one valid input file in the Cue Sheet!");
+ return bUnsupportedTrack ? ErrorUnsupported : ErrorBadFile;
+ }
}
double CueSheetModel::parseTimeIndex(const QString &index)
if(minOK && secOK && frmOK)
{
- return static_cast<double>(60 * min) + static_cast<double>(sec) + ((1.0/75.0) * static_cast<double>(frm));
+ return static_cast<double>(60 * min) + static_cast<double>(sec) + (static_cast<double>(frm) / 75.0);
}
}
QString CueSheetModel::indexToString(const double index) const
{
- int temp = static_cast<int>(index * 100.0);
-
- int msec = temp % 100;
- int secs = temp / 100;
+ if(!_finite(index) || (index < 0.0) || (index > 86400.0))
+ {
+ return QString("??:??.???");
+ }
+
+ QTime time = QTime().addMSecs(static_cast<int>(floor(0.5 + (index * 1000.0))));
- return QString().sprintf("%02d:%02d.%02d", (secs / 60), (secs % 60), msec);
+ if(time.minute() < 100)
+ {
+ return time.toString("mm:ss.zzz");
+ }
+ else
+ {
+ return QString("99:99.999");
+ }
}