OSDN Git Service

Implemented a very basic Cue Sheet parser.
authorlordmulder <mulder2@gmx.de>
Fri, 13 May 2011 00:44:20 +0000 (02:44 +0200)
committerlordmulder <mulder2@gmx.de>
Fri, 13 May 2011 00:44:20 +0000 (02:44 +0200)
gui/CueSheetImport.ui
src/Config.h
src/Dialog_CueImport.cpp
src/Dialog_CueImport.h
src/Dialog_MainWindow.cpp
src/Model_CueSheet.cpp
src/Model_CueSheet.h

index 3953653..9c86aa7 100644 (file)
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>621</width>
-    <height>478</height>
+    <width>700</width>
+    <height>463</height>
    </rect>
   </property>
   <property name="windowTitle">
             </size>
            </property>
            <property name="text">
-            <string>Abort</string>
+            <string>Discard</string>
            </property>
            <property name="icon">
             <iconset resource="../res/Icons.qrc">
   <include location="../res/Images.qrc"/>
   <include location="../res/Images.qrc"/>
   <include location="../res/Images.qrc"/>
+  <include location="../res/Images.qrc"/>
+  <include location="../res/Images.qrc"/>
  </resources>
  <connections>
   <connection>
index 7820a09..99e82b3 100644 (file)
@@ -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
index c720569..d4684c9 100644 (file)
@@ -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("<b>%1</b><br>%2").arg(tr("Import Cue Sheet"), tr("The following Cue Sheet will be split and imported into LameXP.")));
@@ -72,54 +73,53 @@ CueImportDialog::~CueImportDialog(void)
 }
 
 ////////////////////////////////////////////////////////////
+// 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()))
-               {
-                       if((artwork.width() > 256) || (artwork.height() > 256))
-                       {
-                               artwork = artwork.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation);
-                       }
-                       labelArtwork->setPixmap(QPixmap::fromImage(artwork));
-               }
-               else
+               QString errorMsg = tr("An unknown error has occured!");
+               
+               switch(iResult)
                {
-                       qWarning("Error: Failed to load cover art!");
-                       labelArtwork->setPixmap(QPixmap::fromImage(QImage(":/images/CD.png")));
+               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
-       {
-               labelArtwork->setPixmap(QPixmap::fromImage(QImage(":/images/CD.png")));
+               
+               QString text = QString("<nobr>%1</nobr><br><nobr>%2</nobr><br><br><nobr>%3</nobr>").arg(tr("Failed to load the Cue Sheet file:"), cueFile, errorMsg).replace("-", "&minus;");
+               QMessageBox::warning(dynamic_cast<QWidget*>(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!");
index d17dee9..b918055 100644 (file)
@@ -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;
index 2c815bb..acc3437 100644 (file)
@@ -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);
+               }
        )
 }
 
index 70d87e9..4be2e16 100644 (file)
@@ -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<double>(60 * min) + static_cast<double>(sec) + ((1.0/75.0) * static_cast<double>(frm));
+               }
+       }
+
+       qWarning("    Bad time index: '%s'", index.toUtf8().constData());
+       return std::numeric_limits<double>::quiet_NaN();
+}
+
+QString CueSheetModel::indexToString(const double index) const
+{
+       int temp = static_cast<int>(index * 100.0);
+
+       int msec = temp % 100;
+       int secs = temp / 100;
+
+       return QString().sprintf("%02d:%02d.%02d", (secs / 60), (secs % 60), msec);
+}
index 0f23aa3..7cad7aa 100644 (file)
@@ -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<CueSheetFile*> m_files;
 };