diff --git a/gui/CueSheetImport.ui b/gui/CueSheetImport.ui
index 39536534..9c86aa7d 100644
--- a/gui/CueSheetImport.ui
+++ b/gui/CueSheetImport.ui
@@ -6,8 +6,8 @@
0
0
- 621
- 478
+ 700
+ 463
@@ -286,7 +286,7 @@
- Abort
+ Discard
@@ -345,6 +345,8 @@
+
+
diff --git a/src/Config.h b/src/Config.h
index 7820a09d..99e82b38 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -30,7 +30,7 @@
#define VER_LAMEXP_MINOR_LO 2
#define VER_LAMEXP_TYPE Alpha
#define VER_LAMEXP_PATCH 14
-#define VER_LAMEXP_BUILD 505
+#define VER_LAMEXP_BUILD 508
///////////////////////////////////////////////////////////////////////////////
// Tools versions
diff --git a/src/Dialog_CueImport.cpp b/src/Dialog_CueImport.cpp
index c7205691..d4684c9b 100644
--- a/src/Dialog_CueImport.cpp
+++ b/src/Dialog_CueImport.cpp
@@ -49,6 +49,7 @@ CueImportDialog::CueImportDialog(QWidget *parent)
//Create model
m_model = new CueSheetModel();
+ connect(m_model, SIGNAL(modelReset()), this, SLOT(modelChanged()));
//Setup table view
treeView->setModel(m_model);
@@ -57,10 +58,10 @@ CueImportDialog::CueImportDialog(QWidget *parent)
treeView->header()->setResizeMode(1, QHeaderView::Stretch);
treeView->header()->setMovable(false);
treeView->setItemsExpandable(false);
- treeView->expandAll();
//Enable up/down button
connect(imprtButton, SIGNAL(clicked()), this, SLOT(importButtonClicked()));
+ connect(browseButton, SIGNAL(clicked()), this, SLOT(importButtonClicked()));
//Translate
labelHeaderText->setText(QString("%1
%2").arg(tr("Import Cue Sheet"), tr("The following Cue Sheet will be split and imported into LameXP.")));
@@ -71,55 +72,54 @@ CueImportDialog::~CueImportDialog(void)
LAMEXP_DELETE(m_model);
}
+////////////////////////////////////////////////////////////
+// EVENTS
+////////////////////////////////////////////////////////////
+
+void CueImportDialog::showEvent(QShowEvent *event)
+{
+ QDialog::showEvent(event);
+ modelChanged();
+}
+
////////////////////////////////////////////////////////////
// Slots
////////////////////////////////////////////////////////////
int CueImportDialog::exec(const QString &cueFile)
{
- /*
- MetaInfoModel *model = new MetaInfoModel(&audioFile);
- tableView->setModel(model);
- tableView->show();
- frameArtwork->hide();
- setWindowTitle(QString("Meta Information: %1").arg(QFileInfo(audioFile.filePath()).fileName()));
- editButton->setEnabled(true);
- upButton->setEnabled(allowUp);
- downButton->setEnabled(allowDown);
- buttonArtwork->setChecked(false);
-
- if(!audioFile.fileCover().isEmpty())
+ int iResult = m_model->loadCueSheet(cueFile);
+
+ if(iResult)
{
- QImage artwork;
- if(artwork.load(audioFile.fileCover()))
+ QString errorMsg = tr("An unknown error has occured!");
+
+ switch(iResult)
{
- if((artwork.width() > 256) || (artwork.height() > 256))
- {
- artwork = artwork.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- }
- labelArtwork->setPixmap(QPixmap::fromImage(artwork));
+ case 1:
+ errorMsg = tr("The file could not be opened for reading!");
+ break;
+ case 2:
+ errorMsg = tr("The file does not look like a valid Cue Sheet file!");
+ break;
+ case 3:
+ errorMsg = tr("Could not find a supported audio track in the Cue Sheet!");
+ break;
}
- else
- {
- qWarning("Error: Failed to load cover art!");
- labelArtwork->setPixmap(QPixmap::fromImage(QImage(":/images/CD.png")));
- }
- }
- else
- {
- labelArtwork->setPixmap(QPixmap::fromImage(QImage(":/images/CD.png")));
+
+ QString text = QString("%1
%2
%3").arg(tr("Failed to load the Cue Sheet file:"), cueFile, errorMsg).replace("-", "−");
+ QMessageBox::warning(dynamic_cast(parent()), tr("Cue Sheet Error"), text);
+ return iResult;
}
- int iResult = QDialog::exec();
-
- tableView->setModel(NULL);
- LAMEXP_DELETE(model);
-
- return iResult;*/
-
return QDialog::exec();
}
+void CueImportDialog::modelChanged(void)
+{
+ treeView->expandAll();
+}
+
void CueImportDialog::importButtonClicked(void)
{
QMessageBox::information(this, "Not implemenred", "Sorry, not yet. Please try again in a later version!");
diff --git a/src/Dialog_CueImport.h b/src/Dialog_CueImport.h
index d17dee90..b9180559 100644
--- a/src/Dialog_CueImport.h
+++ b/src/Dialog_CueImport.h
@@ -38,8 +38,12 @@ public:
int exec(const QString &cueFile);
+protected:
+ void CueImportDialog::showEvent(QShowEvent *event);
+
private slots:
void importButtonClicked(void);
+ void modelChanged(void);
private:
CueSheetModel *m_model;
diff --git a/src/Dialog_MainWindow.cpp b/src/Dialog_MainWindow.cpp
index 2c815bba..acc3437e 100644
--- a/src/Dialog_MainWindow.cpp
+++ b/src/Dialog_MainWindow.cpp
@@ -2567,9 +2567,13 @@ void MainWindow::importCueSheetActionTriggered(bool checked)
TEMP_HIDE_DROPBOX
(
- CueImportDialog *cueImporter = new CueImportDialog(this);
- cueImporter->exec(QString());
- LAMEXP_DELETE(cueImporter);
+ QString selectedCueFile = QFileDialog::getOpenFileName(this, tr("Open Cue Sheet"), QString(), QString("%1 (*.cue)").arg(tr("Cue Sheet File")));
+ if(!selectedCueFile.isEmpty())
+ {
+ CueImportDialog *cueImporter = new CueImportDialog(this);
+ cueImporter->exec(selectedCueFile);
+ LAMEXP_DELETE(cueImporter);
+ }
)
}
diff --git a/src/Model_CueSheet.cpp b/src/Model_CueSheet.cpp
index 70d87e98..4be2e16f 100644
--- a/src/Model_CueSheet.cpp
+++ b/src/Model_CueSheet.cpp
@@ -19,6 +19,7 @@
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
+#include "Global.h"
#include "Model_CueSheet.h"
#include "Genres.h"
@@ -217,25 +218,28 @@ QVariant CueSheetModel::data(const QModelIndex &index, int role) const
switch(index.column())
{
case 0:
- return tr("Track %1").arg(QString().sprintf("%02d", trackPtr->trackNo() + 1)).append(" ");
+ return tr("Track %1").arg(QString().sprintf("%02d", trackPtr->trackNo())).append(" ");
break;
case 1:
if(!trackPtr->title().isEmpty() && !trackPtr->performer().isEmpty())
{
- return QString("%1 / %2").arg(trackPtr->performer(), trackPtr->title());
+ return QString("%1 - %2").arg(trackPtr->performer(), trackPtr->title());
}
else if(!trackPtr->title().isEmpty())
{
- return trackPtr->title();
+ return QString("%1 - %2").arg(tr("Unknown Artist"), trackPtr->title());
}
else if(!trackPtr->performer().isEmpty())
{
- return trackPtr->performer();
+ return QString("%1 - %2").arg(trackPtr->performer(), tr("Unknown Title"));
+ }
+ else
+ {
+ return QString("%1 - %2").arg(tr("Unknown Artist"), tr("Unknown Title"));
}
- return QVariant();
break;
case 2:
- return QString().sprintf("%07.2f", trackPtr->startIndex());
+ return indexToString(trackPtr->startIndex());
break;
default:
return QVariant();
@@ -253,3 +257,275 @@ void CueSheetModel::clearData(void)
while(!m_files.isEmpty()) delete m_files.takeLast();
endResetModel();
}
+
+////////////////////////////////////////////////////////////
+// Cue Sheet Parser
+////////////////////////////////////////////////////////////
+
+int CueSheetModel::loadCueSheet(const QString &cueFileName)
+{
+ QFile cueFile(cueFileName);
+ if(!cueFile.open(QIODevice::ReadOnly))
+ {
+ return 1;
+ }
+
+ clearData();
+
+ beginResetModel();
+ int iResult = parseCueFile(cueFile);
+ endResetModel();
+
+ return iResult;
+}
+
+int CueSheetModel::parseCueFile(QFile &cueFile)
+{
+ cueFile.seek(0);
+
+ //Check for UTF-8 BOM to guess encoding
+ bool bUTF8 = false;
+ QByteArray bomCheck = cueFile.peek(3);
+ if(bomCheck.size() == 3)
+ {
+ bUTF8 = ((bomCheck.at(0) == '\xef') && (bomCheck.at(1) == '\xbb') && (bomCheck.at(2) == '\xbf'));
+ qDebug("Encoding is %s.", (bUTF8 ? "UTF-8" : "Local 8-Bit"));
+ }
+
+ QRegExp rxFile("^FILE\\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);
+
+ CueSheetFile *currentFile = NULL;
+ CueSheetTrack *currentTrack = NULL;
+
+ bool bPreamble = true;
+ bool bUnsupportedTrack = false;
+
+ QString albumTitle;
+ QString albumPerformer;
+
+ //Loop over the Cue Sheet until all lines were processed
+ while(true)
+ {
+ QByteArray lineData = cueFile.readLine();
+ if(lineData.size() <= 0)
+ {
+ qDebug("End of Cue Sheet file.");
+ break;
+ }
+
+ QString line = bUTF8 ? QString::fromUtf8(lineData.constData(), lineData.size()).trimmed() : QString::fromLocal8Bit(lineData.constData(), lineData.size()).trimmed();
+
+ /* --- FILE --- */
+ if(rxFile.indexIn(line) >= 0)
+ {
+ qDebug("File: <%s> <%s>", 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;
+ }
+ else
+ {
+ LAMEXP_DELETE(currentTrack);
+ }
+ }
+ if(currentFile->trackCount() > 0)
+ {
+ m_files.append(currentFile);
+ currentFile = NULL;
+ }
+ else
+ {
+ LAMEXP_DELETE(currentFile);
+ }
+ }
+ 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));
+ }
+ else
+ {
+ bUnsupportedTrack = true;
+ qWarning("Skipping unsupported file of type '%s'.", rxFile.cap(2).toUtf8().constData());
+ currentFile = NULL;
+ }
+ bPreamble = false;
+ currentTrack = NULL;
+ continue;
+ }
+
+ /* --- TRACK --- */
+ if(rxTrack.indexIn(line) >= 0)
+ {
+ if(currentFile)
+ {
+ qDebug(" Track: <%s> <%s>", 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
+ {
+ LAMEXP_DELETE(currentTrack);
+ }
+ }
+ if(!rxTrack.cap(2).compare("AUDIO", Qt::CaseInsensitive))
+ {
+ currentTrack = new CueSheetTrack(currentFile, rxTrack.cap(1).toInt());
+ }
+ else
+ {
+ bUnsupportedTrack = true;
+ qWarning(" Skipping unsupported track of type '%s'.", rxTrack.cap(2).toUtf8().constData());
+ currentTrack = NULL;
+ }
+ }
+ else
+ {
+ LAMEXP_DELETE(currentTrack);
+ }
+ bPreamble = false;
+ continue;
+ }
+
+ /* --- INDEX --- */
+ if(rxIndex.indexIn(line) >= 0)
+ {
+ if(currentFile && currentTrack)
+ {
+ qDebug(" Index: <%s> <%s>", rxIndex.cap(1).toUtf8().constData(), rxIndex.cap(2).toUtf8().constData());
+ if(rxIndex.cap(1).toInt() == 1)
+ {
+ currentTrack->setStartIndex(parseTimeIndex(rxIndex.cap(2)));
+ }
+ }
+ continue;
+ }
+
+ /* --- TITLE --- */
+ if(rxTitle.indexIn(line) >= 0)
+ {
+ if(bPreamble)
+ {
+ albumTitle = rxTitle.cap(1);
+ }
+ else if(currentFile && currentTrack)
+ {
+ qDebug(" Title: <%s>", rxTitle.cap(1).toUtf8().constData());
+ currentTrack->setTitle(rxTitle.cap(1));
+ }
+ continue;
+ }
+
+ /* --- PERFORMER --- */
+ if(rxPerformer.indexIn(line) >= 0)
+ {
+ if(bPreamble)
+ {
+ albumPerformer = rxPerformer.cap(1);
+ }
+ else if(currentFile && currentTrack)
+ {
+ qDebug(" Title: <%s>", rxPerformer.cap(1).toUtf8().constData());
+ currentTrack->setPerformer(rxPerformer.cap(1));
+ }
+ continue;
+ }
+ }
+
+ //Finally append the very last track/file
+ 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;
+ }
+ else
+ {
+ LAMEXP_DELETE(currentTrack);
+ }
+ }
+ if(currentFile->trackCount() > 0)
+ {
+ m_files.append(currentFile);
+ currentFile = NULL;
+ }
+ else
+ {
+ LAMEXP_DELETE(currentFile);
+ }
+ }
+
+ return (m_files.count() > 0) ? 0 : (bUnsupportedTrack ? 3 : 2);
+}
+
+double CueSheetModel::parseTimeIndex(const QString &index)
+{
+ QRegExp rxTimeIndex("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*:\\s*(\\d+)\\s*");
+
+ if(rxTimeIndex.indexIn(index) >= 0)
+ {
+ int min, sec, frm;
+ bool minOK, secOK, frmOK;
+
+ min = rxTimeIndex.cap(1).toInt(&minOK);
+ sec = rxTimeIndex.cap(2).toInt(&secOK);
+ frm = rxTimeIndex.cap(3).toInt(&frmOK);
+
+ if(minOK && secOK && frmOK)
+ {
+ return static_cast(60 * min) + static_cast(sec) + ((1.0/75.0) * static_cast(frm));
+ }
+ }
+
+ qWarning(" Bad time index: '%s'", index.toUtf8().constData());
+ return std::numeric_limits::quiet_NaN();
+}
+
+QString CueSheetModel::indexToString(const double index) const
+{
+ int temp = static_cast(index * 100.0);
+
+ int msec = temp % 100;
+ int secs = temp / 100;
+
+ return QString().sprintf("%02d:%02d.%02d", (secs / 60), (secs % 60), msec);
+}
diff --git a/src/Model_CueSheet.h b/src/Model_CueSheet.h
index 0f23aa33..7cad7aa8 100644
--- a/src/Model_CueSheet.h
+++ b/src/Model_CueSheet.h
@@ -44,6 +44,12 @@ public:
QModelIndex parent(const QModelIndex &child) const;
void clearData(void);
+ //Cue Sheet functions
+ int loadCueSheet(const QString &cueFile);
+
private:
+ int parseCueFile(QFile &cueFile);
+ double parseTimeIndex(const QString &index);
+ QString indexToString(const double index) const;
QList m_files;
};