1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
23 #include "Model_CueSheet.h"
26 #include <QApplication>
32 #include <QTextStream>
37 #define UNQUOTE(STR) STR.split("\"", QString::SkipEmptyParts).first().trimmed()
39 ////////////////////////////////////////////////////////////
41 ////////////////////////////////////////////////////////////
46 virtual const char* type(void) = 0;
47 virtual bool isValid(void) { return false; }
50 class CueSheetTrack
: public CueSheetItem
53 CueSheetTrack(CueSheetFile
*parent
, int trackNo
)
58 m_startIndex
= std::numeric_limits
<double>::quiet_NaN();
59 m_duration
= std::numeric_limits
<double>::infinity();
62 int trackNo(void) { return m_trackNo
; }
63 double startIndex(void) { return m_startIndex
; }
64 double duration(void) { return m_duration
; }
65 QString
title(void) { return m_title
; }
66 QString
performer(void) { return m_performer
; }
67 QString
genre(void) { return m_genre
; }
68 unsigned int year(void) { return m_year
; }
69 CueSheetFile
*parent(void) { return m_parent
; }
70 void setStartIndex(double startIndex
) { m_startIndex
= startIndex
; }
71 void setDuration(double duration
) { m_duration
= duration
; }
72 void setTitle(const QString
&title
, bool update
= false) { if(!update
|| (m_title
.isEmpty() && !title
.isEmpty())) m_title
= title
; }
73 void setPerformer(const QString
&performer
, bool update
= false) { if(!update
|| (m_performer
.isEmpty() && !performer
.isEmpty())) m_performer
= performer
; }
74 void setGenre(const QString
&genre
, bool update
= false) { if(!update
|| (m_genre
.isEmpty() && !m_genre
.isEmpty())) m_genre
= genre
; }
75 void setYear(const unsigned int year
, bool update
= false) { if(!update
|| (year
== 0)) m_year
= year
; }
76 virtual bool isValid(void) { return !(_isnan(m_startIndex
) || (m_trackNo
< 0)); }
77 virtual const char* type(void) { return "CueSheetTrack"; }
86 CueSheetFile
*m_parent
;
89 class CueSheetFile
: public CueSheetItem
92 CueSheetFile(const QString
&fileName
) : m_fileName(fileName
) {}
93 ~CueSheetFile(void) { while(!m_tracks
.isEmpty()) delete m_tracks
.takeLast(); }
94 QString
fileName(void) { return m_fileName
; }
95 void addTrack(CueSheetTrack
*track
) { m_tracks
.append(track
); }
96 void clearTracks(void) { while(!m_tracks
.isEmpty()) delete m_tracks
.takeLast(); }
97 CueSheetTrack
*track(int index
) { return m_tracks
.at(index
); }
98 int trackCount(void) { return m_tracks
.count(); }
99 virtual bool isValid(void) { return m_tracks
.count() > 0; }
100 virtual const char* type(void) { return "CueSheetFile"; }
102 const QString m_fileName
;
103 QList
<CueSheetTrack
*> m_tracks
;
106 ////////////////////////////////////////////////////////////
107 // Constructor & Destructor
108 ////////////////////////////////////////////////////////////
110 QMutex
CueSheetModel::m_mutex(QMutex::Recursive
);
112 CueSheetModel::CueSheetModel()
114 m_fileIcon(":/icons/music.png"),
115 m_trackIcon(":/icons/control_play_blue.png")
120 for(int i
= 0; i
< 5; i
++)
122 CueSheetFile
*currentFile
= new CueSheetFile(QString().sprintf("File %02d.wav", i
+1));
123 for(int j
= 0; j
< 8; j
++)
125 CueSheetTrack
*currentTrack
= new CueSheetTrack(currentFile
, trackNo
++);
126 currentTrack
->setTitle("ATWA (Air Trees Water Animals)");
127 currentTrack
->setPerformer("System of a Down");
128 currentFile
->addTrack(currentTrack
);
130 m_files
.append(currentFile
);
134 CueSheetModel::~CueSheetModel(void)
136 while(!m_files
.isEmpty()) delete m_files
.takeLast();
139 ////////////////////////////////////////////////////////////
141 ////////////////////////////////////////////////////////////
143 QModelIndex
CueSheetModel::index(int row
, int column
, const QModelIndex
&parent
) const
145 QMutexLocker
lock(&m_mutex
);
147 if(!parent
.isValid())
149 return (row
< m_files
.count()) ? createIndex(row
, column
, m_files
.at(row
)) : QModelIndex();
152 CueSheetItem
*parentItem
= static_cast<CueSheetItem
*>(parent
.internalPointer());
153 if(CueSheetFile
*filePtr
= dynamic_cast<CueSheetFile
*>(parentItem
))
155 return (row
< filePtr
->trackCount()) ? createIndex(row
, column
, filePtr
->track(row
)) : QModelIndex();
158 return QModelIndex();
161 int CueSheetModel::columnCount(const QModelIndex
&parent
) const
163 QMutexLocker
lock(&m_mutex
);
167 int CueSheetModel::rowCount(const QModelIndex
&parent
) const
169 QMutexLocker
lock(&m_mutex
);
171 if(!parent
.isValid())
173 return m_files
.count();
176 CueSheetItem
*parentItem
= static_cast<CueSheetItem
*>(parent
.internalPointer());
177 if(CueSheetFile
*filePtr
= dynamic_cast<CueSheetFile
*>(parentItem
))
179 return filePtr
->trackCount();
185 QModelIndex
CueSheetModel::parent(const QModelIndex
&child
) const
187 QMutexLocker
lock(&m_mutex
);
191 CueSheetItem
*childItem
= static_cast<CueSheetItem
*>(child
.internalPointer());
192 if(CueSheetTrack
*trackPtr
= dynamic_cast<CueSheetTrack
*>(childItem
))
194 return createIndex(m_files
.indexOf(trackPtr
->parent()), 0, trackPtr
->parent());
198 return QModelIndex();
201 QVariant
CueSheetModel::headerData (int section
, Qt::Orientation orientation
, int role
) const
203 QMutexLocker
lock(&m_mutex
);
205 if(role
== Qt::DisplayRole
)
213 return tr("File / Track");
219 return tr("Duration");
232 QVariant
CueSheetModel::data(const QModelIndex
&index
, int role
) const
234 QMutexLocker
lock(&m_mutex
);
236 if(role
== Qt::DisplayRole
)
238 CueSheetItem
*item
= reinterpret_cast<CueSheetItem
*>(index
.internalPointer());
240 if(CueSheetFile
*filePtr
= dynamic_cast<CueSheetFile
*>(item
))
242 switch(index
.column())
245 return tr("File %1").arg(QString().sprintf("%02d", index
.row() + 1)).append(" ");
248 return QFileInfo(filePtr
->fileName()).fileName();
255 else if(CueSheetTrack
*trackPtr
= dynamic_cast<CueSheetTrack
*>(item
))
257 switch(index
.column())
260 return tr("Track %1").arg(QString().sprintf("%02d", trackPtr
->trackNo())).append(" ");
263 if(!trackPtr
->title().isEmpty() && !trackPtr
->performer().isEmpty())
265 return QString("%1 - %2").arg(trackPtr
->performer(), trackPtr
->title());
267 else if(!trackPtr
->title().isEmpty())
269 return QString("%1 - %2").arg(tr("Unknown Artist"), trackPtr
->title());
271 else if(!trackPtr
->performer().isEmpty())
273 return QString("%1 - %2").arg(trackPtr
->performer(), tr("Unknown Title"));
277 return QString("%1 - %2").arg(tr("Unknown Artist"), tr("Unknown Title"));
281 return indexToString(trackPtr
->startIndex());
284 return indexToString(trackPtr
->duration());
292 else if(role
== Qt::ToolTipRole
)
294 CueSheetItem
*item
= reinterpret_cast<CueSheetItem
*>(index
.internalPointer());
296 if(CueSheetFile
*filePtr
= dynamic_cast<CueSheetFile
*>(item
))
298 return QDir::toNativeSeparators(filePtr
->fileName());
300 else if(CueSheetTrack
*trackPtr
= dynamic_cast<CueSheetTrack
*>(item
))
302 return QDir::toNativeSeparators(trackPtr
->parent()->fileName());
305 else if(role
== Qt::DecorationRole
)
307 if(index
.column() == 0)
309 CueSheetItem
*item
= reinterpret_cast<CueSheetItem
*>(index
.internalPointer());
311 if(dynamic_cast<CueSheetFile
*>(item
))
315 else if(dynamic_cast<CueSheetTrack
*>(item
))
321 else if(role
== Qt::FontRole
)
323 QFont
font("Monospace");
324 font
.setStyleHint(QFont::TypeWriter
);
325 if((index
.column() == 1))
327 CueSheetItem
*item
= reinterpret_cast<CueSheetItem
*>(index
.internalPointer());
328 font
.setBold(dynamic_cast<CueSheetFile
*>(item
) != NULL
);
332 else if(role
== Qt::ForegroundRole
)
334 if((index
.column() == 1))
336 CueSheetItem
*item
= reinterpret_cast<CueSheetItem
*>(index
.internalPointer());
337 if(CueSheetFile
*filePtr
= dynamic_cast<CueSheetFile
*>(item
))
339 return (QFileInfo(filePtr
->fileName()).size() > 4) ? QColor("mediumblue") : QColor("darkred");
342 else if((index
.column() == 3))
344 CueSheetItem
*item
= reinterpret_cast<CueSheetItem
*>(index
.internalPointer());
345 if(CueSheetTrack
*trackPtr
= dynamic_cast<CueSheetTrack
*>(item
))
347 if(trackPtr
->duration() == std::numeric_limits
<double>::infinity())
349 return QColor("dimgrey");
358 void CueSheetModel::clearData(void)
360 QMutexLocker
lock(&m_mutex
);
363 while(!m_files
.isEmpty()) delete m_files
.takeLast();
367 ////////////////////////////////////////////////////////////
369 ////////////////////////////////////////////////////////////
371 int CueSheetModel::getFileCount(void)
373 QMutexLocker
lock(&m_mutex
);
374 return m_files
.count();
377 QString
CueSheetModel::getFileName(int fileIndex
)
379 QMutexLocker
lock(&m_mutex
);
381 if(fileIndex
< 0 || fileIndex
>= m_files
.count())
386 return m_files
.at(fileIndex
)->fileName();
389 int CueSheetModel::getTrackCount(int fileIndex
)
391 QMutexLocker
lock(&m_mutex
);
393 if(fileIndex
< 0 || fileIndex
>= m_files
.count())
398 return m_files
.at(fileIndex
)->trackCount();
401 int CueSheetModel::getTrackNo(int fileIndex
, int trackIndex
)
403 QMutexLocker
lock(&m_mutex
);
405 if(fileIndex
>= 0 && fileIndex
< m_files
.count())
407 CueSheetFile
*currentFile
= m_files
.at(fileIndex
);
408 if(trackIndex
>= 0 && trackIndex
< currentFile
->trackCount())
410 return currentFile
->track(trackIndex
)->trackNo();
417 void CueSheetModel::getTrackIndex(int fileIndex
, int trackIndex
, double *startIndex
, double *duration
)
419 QMutexLocker
lock(&m_mutex
);
421 *startIndex
= std::numeric_limits
<double>::quiet_NaN();
422 *duration
= std::numeric_limits
<double>::quiet_NaN();
424 if(fileIndex
>= 0 && fileIndex
< m_files
.count())
426 CueSheetFile
*currentFile
= m_files
.at(fileIndex
);
427 if(trackIndex
>= 0 && trackIndex
< currentFile
->trackCount())
429 CueSheetTrack
*currentTrack
= currentFile
->track(trackIndex
);
430 *startIndex
= currentTrack
->startIndex();
431 *duration
= currentTrack
->duration();
436 QString
CueSheetModel::getTrackPerformer(int fileIndex
, int trackIndex
)
438 QMutexLocker
lock(&m_mutex
);
440 if(fileIndex
>= 0 && fileIndex
< m_files
.count())
442 CueSheetFile
*currentFile
= m_files
.at(fileIndex
);
443 if(trackIndex
>= 0 && trackIndex
< currentFile
->trackCount())
445 CueSheetTrack
*currentTrack
= currentFile
->track(trackIndex
);
446 return currentTrack
->performer();
453 QString
CueSheetModel::getTrackTitle(int fileIndex
, int trackIndex
)
455 QMutexLocker
lock(&m_mutex
);
457 if(fileIndex
>= 0 && fileIndex
< m_files
.count())
459 CueSheetFile
*currentFile
= m_files
.at(fileIndex
);
460 if(trackIndex
>= 0 && trackIndex
< currentFile
->trackCount())
462 CueSheetTrack
*currentTrack
= currentFile
->track(trackIndex
);
463 return currentTrack
->title();
470 QString
CueSheetModel::getTrackGenre(int fileIndex
, int trackIndex
)
472 QMutexLocker
lock(&m_mutex
);
474 if(fileIndex
>= 0 && fileIndex
< m_files
.count())
476 CueSheetFile
*currentFile
= m_files
.at(fileIndex
);
477 if(trackIndex
>= 0 && trackIndex
< currentFile
->trackCount())
479 CueSheetTrack
*currentTrack
= currentFile
->track(trackIndex
);
480 return currentTrack
->genre();
487 unsigned int CueSheetModel::getTrackYear(int fileIndex
, int trackIndex
)
489 QMutexLocker
lock(&m_mutex
);
491 if(fileIndex
>= 0 && fileIndex
< m_files
.count())
493 CueSheetFile
*currentFile
= m_files
.at(fileIndex
);
494 if(trackIndex
>= 0 && trackIndex
< currentFile
->trackCount())
496 CueSheetTrack
*currentTrack
= currentFile
->track(trackIndex
);
497 return currentTrack
->year();
504 QString
CueSheetModel::getAlbumPerformer(void)
506 QMutexLocker
lock(&m_mutex
);
507 return m_albumPerformer
;
510 QString
CueSheetModel::getAlbumTitle(void)
512 QMutexLocker
lock(&m_mutex
);
516 QString
CueSheetModel::getAlbumGenre(void)
518 QMutexLocker
lock(&m_mutex
);
522 unsigned int CueSheetModel::getAlbumYear(void)
524 QMutexLocker
lock(&m_mutex
);
527 ////////////////////////////////////////////////////////////
529 ////////////////////////////////////////////////////////////
531 int CueSheetModel::loadCueSheet(const QString
&cueFileName
, QCoreApplication
*application
, QTextCodec
*forceCodec
)
533 QMutexLocker
lock(&m_mutex
);
534 const QTextCodec
*codec
= (forceCodec
!= NULL
) ? forceCodec
: QTextCodec::codecForName("System");
536 QFile
cueFile(cueFileName
);
537 if(!cueFile
.open(QIODevice::ReadOnly
))
539 return ErrorIOFailure
;
545 int iResult
= parseCueFile(cueFile
, QDir(QFileInfo(cueFile
).canonicalPath()), application
, codec
);
551 int CueSheetModel::parseCueFile(QFile
&cueFile
, const QDir
&baseDir
, QCoreApplication
*application
, const QTextCodec
*codec
)
554 qDebug("\n[Cue Sheet Import]");
555 bool bForceLatin1
= false;
557 //Reject very large files, as parsing might take until forever
558 if(cueFile
.size() >= 10485760i64
)
560 qWarning("File is very big. Probably not a Cue Sheet. Rejecting...");
564 //Test selected Codepage for decoding errors
565 qDebug("Character encoding is: %s.", codec
->name().constData());
566 const QString replacementSymbol
= QString(QChar(QChar::ReplacementCharacter
));
567 QByteArray testData
= cueFile
.peek(1048576);
568 if((!testData
.isEmpty()) && codec
->toUnicode(testData
.constData(), testData
.size()).contains(replacementSymbol
))
570 qWarning("Decoding error using selected codepage (%s). Enforcing Latin-1.", codec
->name().constData());
576 QTextStream
cueStream(&cueFile
);
577 cueStream
.setAutoDetectUnicode(false);
578 cueStream
.setCodec(bForceLatin1
? "latin1" : codec
->name());
579 cueStream
.seek(0i64
);
581 //Create regular expressions
582 QRegExp
rxFile("^FILE\\s+(\"[^\"]+\"|\\S+)\\s+(\\w+)$", Qt::CaseInsensitive
);
583 QRegExp
rxTrack("^TRACK\\s+(\\d+)\\s(\\w+)$", Qt::CaseInsensitive
);
584 QRegExp
rxIndex("^INDEX\\s+(\\d+)\\s+([0-9:]+)$", Qt::CaseInsensitive
);
585 QRegExp
rxTitle("^TITLE\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive
);
586 QRegExp
rxPerformer("^PERFORMER\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive
);
587 QRegExp
rxGenre("^REM\\s+GENRE\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive
);
588 QRegExp
rxYear("^REM\\s+DATE\\s+(\\d+)$", Qt::CaseInsensitive
);
590 bool bPreamble
= true;
591 bool bUnsupportedTrack
= false;
593 CueSheetFile
*currentFile
= NULL
;
594 CueSheetTrack
*currentTrack
= NULL
;
596 m_albumTitle
.clear();
597 m_albumPerformer
.clear();
598 m_albumGenre
.clear();
601 //Loop over the Cue Sheet until all lines were processed
602 for(int lines
= 0; lines
< INT_MAX
; lines
++)
606 application
->processEvents();
607 if(lines
< 128) Sleep(10);
610 if(cueStream
.atEnd())
612 qDebug("End of Cue Sheet file.");
616 QString line
= cueStream
.readLine().trimmed();
619 if(rxFile
.indexIn(line
) >= 0)
621 qDebug("%03d File: <%s> <%s>", lines
, rxFile
.cap(1).toUtf8().constData(), rxFile
.cap(2).toUtf8().constData());
626 if(currentTrack
->isValid())
628 currentFile
->addTrack(currentTrack
);
633 LAMEXP_DELETE(currentTrack
);
636 if(currentFile
->isValid())
638 m_files
.append(currentFile
);
643 LAMEXP_DELETE(currentFile
);
648 LAMEXP_DELETE(currentTrack
);
650 if(!rxFile
.cap(2).compare("WAVE", Qt::CaseInsensitive
) || !rxFile
.cap(2).compare("MP3", Qt::CaseInsensitive
) || !rxFile
.cap(2).compare("AIFF", Qt::CaseInsensitive
))
652 currentFile
= new CueSheetFile(baseDir
.absoluteFilePath(UNQUOTE(rxFile
.cap(1))));
653 qDebug("%03d File path: <%s>", lines
, currentFile
->fileName().toUtf8().constData());
657 bUnsupportedTrack
= true;
658 qWarning("%03d Skipping unsupported file of type '%s'.", lines
, rxFile
.cap(2).toUtf8().constData());
667 if(rxTrack
.indexIn(line
) >= 0)
671 qDebug("%03d Track: <%s> <%s>", lines
, rxTrack
.cap(1).toUtf8().constData(), rxTrack
.cap(2).toUtf8().constData());
674 if(currentTrack
->isValid())
676 currentFile
->addTrack(currentTrack
);
681 LAMEXP_DELETE(currentTrack
);
684 if(!rxTrack
.cap(2).compare("AUDIO", Qt::CaseInsensitive
))
686 currentTrack
= new CueSheetTrack(currentFile
, rxTrack
.cap(1).toInt());
690 bUnsupportedTrack
= true;
691 qWarning("%03d Skipping unsupported track of type '%s'.", lines
, rxTrack
.cap(2).toUtf8().constData());
697 LAMEXP_DELETE(currentTrack
);
704 if(rxIndex
.indexIn(line
) >= 0)
706 if(currentFile
&& currentTrack
)
708 qDebug("%03d Index: <%s> <%s>", lines
, rxIndex
.cap(1).toUtf8().constData(), rxIndex
.cap(2).toUtf8().constData());
709 if(rxIndex
.cap(1).toInt() == 1)
711 currentTrack
->setStartIndex(parseTimeIndex(rxIndex
.cap(2)));
718 if(rxTitle
.indexIn(line
) >= 0)
722 m_albumTitle
= UNQUOTE(rxTitle
.cap(1)).simplified();
724 else if(currentFile
&& currentTrack
)
726 qDebug("%03d Title: <%s>", lines
, rxTitle
.cap(1).toUtf8().constData());
727 currentTrack
->setTitle(UNQUOTE(rxTitle
.cap(1)).simplified());
732 /* --- PERFORMER --- */
733 if(rxPerformer
.indexIn(line
) >= 0)
737 m_albumPerformer
= UNQUOTE(rxPerformer
.cap(1)).simplified();
739 else if(currentFile
&& currentTrack
)
741 qDebug("%03d Title: <%s>", lines
, rxPerformer
.cap(1).toUtf8().constData());
742 currentTrack
->setPerformer(UNQUOTE(rxPerformer
.cap(1)).simplified());
748 if(rxGenre
.indexIn(line
) >= 0)
752 QString temp
= UNQUOTE(rxGenre
.cap(1)).simplified();
753 for(int i
= 0; g_lamexp_generes
[i
]; i
++)
755 if(temp
.compare(g_lamexp_generes
[i
], Qt::CaseInsensitive
) == 0)
757 m_albumGenre
= QString(g_lamexp_generes
[i
]);
762 else if(currentFile
&& currentTrack
)
764 qDebug("%03d Genre: <%s>", lines
, rxGenre
.cap(1).toUtf8().constData());
765 QString temp
= UNQUOTE(rxGenre
.cap(1).simplified());
766 for(int i
= 0; g_lamexp_generes
[i
]; i
++)
768 if(temp
.compare(g_lamexp_generes
[i
], Qt::CaseInsensitive
) == 0)
770 currentTrack
->setGenre(QString(g_lamexp_generes
[i
]));
779 if(rxYear
.indexIn(line
) >= 0)
784 unsigned int temp
= rxYear
.cap(1).toUInt(&ok
);
785 if(ok
) m_albumYear
= temp
;
787 else if(currentFile
&& currentTrack
)
789 qDebug("%03d Year: <%s>", lines
, rxPerformer
.cap(1).toUtf8().constData());
791 unsigned int temp
= rxYear
.cap(1).toUInt(&ok
);
792 if(ok
) currentTrack
->setYear(temp
);
798 //Append the very last track/file that is still pending
803 if(currentTrack
->isValid())
805 currentFile
->addTrack(currentTrack
);
810 LAMEXP_DELETE(currentTrack
);
813 if(currentFile
->isValid())
815 m_files
.append(currentFile
);
820 LAMEXP_DELETE(currentFile
);
824 //Finally calculate duration of each track
825 int nFiles
= m_files
.count();
826 for(int i
= 0; i
< nFiles
; i
++)
830 application
->processEvents();
834 CueSheetFile
*currentFile
= m_files
.at(i
);
835 int nTracks
= currentFile
->trackCount();
838 for(int j
= 1; j
< nTracks
; j
++)
840 CueSheetTrack
*currentTrack
= currentFile
->track(j
);
841 CueSheetTrack
*previousTrack
= currentFile
->track(j
-1);
842 double duration
= currentTrack
->startIndex() - previousTrack
->startIndex();
843 previousTrack
->setDuration(qMax(0.0, duration
));
848 //Sanity check of track numbers
851 bool hasTracks
= false;
852 int previousTrackNo
= -1;
854 for(int i
= 0; i
< 100; i
++)
859 for(int i
= 0; i
< nFiles
; i
++)
863 application
->processEvents();
866 CueSheetFile
*currentFile
= m_files
.at(i
);
867 int nTracks
= currentFile
->trackCount();
870 for(int j
= 0; j
< nTracks
; j
++)
872 int currentTrackNo
= currentFile
->track(j
)->trackNo();
873 if(currentTrackNo
> 99)
875 qWarning("Track #%02d is invalid (maximum is 99), Cue Sheet is inconsistent!", currentTrackNo
);
876 return ErrorInconsistent
;
878 if(currentTrackNo
<= previousTrackNo
)
880 qWarning("Non-increasing track numbers (%02d -> %02d), Cue Sheet is inconsistent!", previousTrackNo
, currentTrackNo
);
881 return ErrorInconsistent
;
883 if(trackNo
[currentTrackNo
])
885 qWarning("Track #%02d exists multiple times, Cue Sheet is inconsistent!", currentTrackNo
);
886 return ErrorInconsistent
;
888 trackNo
[currentTrackNo
] = true;
889 previousTrackNo
= currentTrackNo
;
897 qWarning("Could not find at least one valid track in the Cue Sheet!");
898 return ErrorInconsistent
;
905 qWarning("Could not find at least one valid input file in the Cue Sheet!");
906 return bUnsupportedTrack
? ErrorUnsupported
: ErrorBadFile
;
910 double CueSheetModel::parseTimeIndex(const QString
&index
)
912 QRegExp
rxTimeIndex("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*:\\s*(\\d+)\\s*");
914 if(rxTimeIndex
.indexIn(index
) >= 0)
917 bool minOK
, secOK
, frmOK
;
919 min
= rxTimeIndex
.cap(1).toInt(&minOK
);
920 sec
= rxTimeIndex
.cap(2).toInt(&secOK
);
921 frm
= rxTimeIndex
.cap(3).toInt(&frmOK
);
923 if(minOK
&& secOK
&& frmOK
)
925 return static_cast<double>(60 * min
) + static_cast<double>(sec
) + (static_cast<double>(frm
) / 75.0);
929 qWarning(" Bad time index: '%s'", index
.toUtf8().constData());
930 return std::numeric_limits
<double>::quiet_NaN();
933 QString
CueSheetModel::indexToString(const double index
) const
935 if(!_finite(index
) || (index
< 0.0) || (index
> 86400.0))
937 return QString("??:??.???");
940 QTime time
= QTime().addMSecs(static_cast<int>(floor(0.5 + (index
* 1000.0))));
942 if(time
.minute() < 100)
944 return time
.toString("mm:ss.zzz");
948 return QString("99:99.999");