diff options
| -rw-r--r-- | src/plugins/Input/flac/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/plugins/Input/flac/cueparser.cpp | 194 | ||||
| -rw-r--r-- | src/plugins/Input/flac/cueparser.h | 59 | ||||
| -rw-r--r-- | src/plugins/Input/flac/decoder_flac.cpp | 94 | ||||
| -rw-r--r-- | src/plugins/Input/flac/decoder_flac.h | 7 | ||||
| -rw-r--r-- | src/plugins/Input/flac/decoderflacfactory.cpp | 24 | ||||
| -rw-r--r-- | src/plugins/Input/flac/flac.pro | 12 |
7 files changed, 363 insertions, 29 deletions
diff --git a/src/plugins/Input/flac/CMakeLists.txt b/src/plugins/Input/flac/CMakeLists.txt index 9e41e52f7..080629d99 100644 --- a/src/plugins/Input/flac/CMakeLists.txt +++ b/src/plugins/Input/flac/CMakeLists.txt @@ -40,12 +40,14 @@ SET(libflac_SRCS decoder_flac.cpp decoderflacfactory.cpp detailsdialog.cpp + cueparser.cpp ) SET(libflac_MOC_HDRS decoderflacfactory.h decoder_flac.h detailsdialog.h + cueparser.h ) SET(libflac_RCCS translations/translations.qrc) diff --git a/src/plugins/Input/flac/cueparser.cpp b/src/plugins/Input/flac/cueparser.cpp new file mode 100644 index 000000000..64870c97f --- /dev/null +++ b/src/plugins/Input/flac/cueparser.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (C) 2008 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include <QDir> +#include <QSettings> +#include <QTextStream> +#include <QTextCodec> + +#include <qmmp/decoder.h> + +#include "cueparser.h" + +CUEParser::CUEParser(const QByteArray &array, const QString &fileName) +{ + QString album, genre, date, comment; + QTextStream textStream (array); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + QTextCodec *codec = QTextCodec::codecForName(settings.value("CUE/encoding","ISO-8859-1").toByteArray ()); + textStream.setCodec(codec); + m_filePath = fileName; + QString artist; + while (!textStream.atEnd()) + { + QString line = textStream.readLine().trimmed(); + QStringList words = splitLine(line); + if (words.size() < 2) + continue; + + if (words[0] == "FILE") + { + //m_filePath = QUrl(fileName).path (); + //m_filePath = QFileInfo(m_filePath).dir().filePath(words[1]); + } + else if (words[0] == "PERFORMER") + { + if (m_infoList.isEmpty()) + { + artist = words[1]; + continue; + } + else + m_infoList.last().setMetaData(Qmmp::ARTIST, words[1]); + + } + else if (words[0] == "TITLE") + { + if (m_infoList.isEmpty()) + album = words[1]; + else + m_infoList.last().setMetaData(Qmmp::TITLE, words[1]); + } + else if (words[0] == "TRACK") + { + FileInfo info("flac://" + fileName + QString("#%1").arg(words[1].toInt())); + info.setMetaData(Qmmp::TRACK, words[1].toInt()); + m_infoList << info; + m_offsets << 0; + } + else if (words[0] == "INDEX") + { + if (m_infoList.isEmpty()) + continue; + m_infoList.last ().setLength(getLength(words[2])); + m_offsets.last() = getLength(words[2]); + } + else if (words[0] == "REM") + { + if (words.size() < 3) + continue; + if (words[1] == "GENRE") + genre = words[2]; + else if (words[1] == "DATE") + date = words[2]; + else if (words[1] == "COMMENT") + comment = words[2]; + } + } + //calculate length + for (int i = 0; i < m_infoList.size() - 1; ++i) + m_infoList[i].setLength(m_infoList[i+1].length() - m_infoList[i].length()); + //calculate last item length + QList <FileInfo *> f_list; + //f_list = Decoder::createPlayList(m_filePath); + qint64 l = f_list.isEmpty() ? 0 : f_list.at(0)->length(); + if (l > m_infoList.last().length()) + m_infoList.last().setLength(l - m_infoList.last().length()); + else + m_infoList.last().setLength(0); + + for (int i = 0; i < m_infoList.size(); ++i) + { + m_infoList[i].setMetaData(Qmmp::ALBUM, album); + m_infoList[i].setMetaData(Qmmp::GENRE, genre); + m_infoList[i].setMetaData(Qmmp::YEAR, date); + m_infoList[i].setMetaData(Qmmp::COMMENT, comment); + if(!m_infoList[i].metaData().count(Qmmp::ARTIST) && !artist.isEmpty()) + m_infoList[i].setMetaData(Qmmp::ARTIST, artist); + } +} + + +CUEParser::~CUEParser() +{ +} + +QList<FileInfo*> CUEParser::createPlayList() +{ + QList<FileInfo*> list; + foreach(FileInfo info, m_infoList) + { + list << new FileInfo(info); + } + return list; +} + +const QString CUEParser::filePath() +{ + return m_filePath; +} + +qint64 CUEParser::offset(int track) +{ + return m_offsets.at(track - 1); +} + +qint64 CUEParser::length(int track) +{ + return m_infoList.at(track - 1).length(); +} + +int CUEParser::count() +{ + return m_infoList.count(); +} + +FileInfo *CUEParser::info(int track) +{ + return &m_infoList[track - 1]; +} + +QStringList CUEParser::splitLine(const QString &line) +{ + //qDebug("row string = %s",qPrintable(line)); + QStringList list; + QString buf = line.trimmed(); + if (buf.isEmpty()) + return list; + while (!buf.isEmpty()) + { + //qDebug(qPrintable(buf)); + if (buf.startsWith('"')) + { + int end = buf.indexOf('"',1); + list << buf.mid (1, end - 1); + buf.remove (0, end+1); + } + else + { + int end = buf.indexOf(' ', 0); + if (end < 0) + end = buf.size(); + list << buf.mid (0, end); + buf.remove (0, end); + } + buf = buf.trimmed(); + } + return list; +} + +int CUEParser::getLength(const QString &str) +{ + QStringList list = str.split(":"); + if (list.size() < 2) + return 0; + return list.at(0).toInt()*60 + list.at(1).toInt(); +} diff --git a/src/plugins/Input/flac/cueparser.h b/src/plugins/Input/flac/cueparser.h new file mode 100644 index 000000000..365076c9a --- /dev/null +++ b/src/plugins/Input/flac/cueparser.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2008 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef CUEPARSER_H +#define CUEPARSER_H + +#include <QList> +#include <QMap> +#include <QByteArray> +#include <QString> +#include <QStringList> + +#include <qmmp/fileinfo.h> + + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class CUEParser +{ +public: + CUEParser(const QByteArray &array, const QString &fileName); + + ~CUEParser(); + + QList<FileInfo*> createPlayList(); + const QString filePath(); + qint64 offset(int track); + qint64 length(int track); + int count(); + FileInfo *info(int track); + +private: + QList< QMap<int, int> > m_map; + QString m_filePath; + QList <FileInfo> m_infoList; + QList <int> m_offsets; + QStringList splitLine(const QString &line); + int getLength(const QString &str); + +}; + +#endif diff --git a/src/plugins/Input/flac/decoder_flac.cpp b/src/plugins/Input/flac/decoder_flac.cpp index 5bb2b6219..44618c919 100644 --- a/src/plugins/Input/flac/decoder_flac.cpp +++ b/src/plugins/Input/flac/decoder_flac.cpp @@ -23,14 +23,23 @@ and libxmms-flac written by Josh Coalson. */ +#include <taglib/tag.h> +#include <taglib/fileref.h> +#include <taglib/flacfile.h> +#include <taglib/xiphcomment.h> +#include <taglib/tmap.h> + #include <qmmp/constants.h> #include <qmmp/buffer.h> #include <qmmp/output.h> #include <qmmp/recycler.h> #include <QObject> +#include <QFile> #include <QIODevice> #include <FLAC/all.h> + +#include "cueparser.h" #include "decoder_flac.h" @@ -146,7 +155,7 @@ static FLAC__StreamDecoderReadStatus flac_callback_read (const FLAC__StreamDecod DecoderFLAC *dflac = (DecoderFLAC *) client_data; qint64 res; - res = dflac->input()->read((char *)buffer, *bytes); + res = dflac->data()->input->read((char *)buffer, *bytes); if (res > 0) { @@ -189,7 +198,7 @@ static FLAC__StreamDecoderTellStatus flac_callback_tell (const FLAC__StreamDecod void *client_data) { DecoderFLAC *dflac = (DecoderFLAC *) client_data; - *offset = dflac->input()->pos (); + *offset = dflac->data()->input->pos (); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } @@ -199,7 +208,7 @@ static FLAC__StreamDecoderSeekStatus flac_callback_seek (const FLAC__StreamDecod { DecoderFLAC *dflac = (DecoderFLAC *) client_data; - return dflac->input()->seek(offset) + return dflac->data()->input->seek(offset) ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; } @@ -209,7 +218,7 @@ static FLAC__StreamDecoderLengthStatus flac_callback_length (const FLAC__StreamD void *client_data) { DecoderFLAC *dflac = (DecoderFLAC *) client_data; - *stream_length = dflac->input()->size(); + *stream_length = dflac->data()->input->size(); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } @@ -248,7 +257,7 @@ static void flac_callback_error (const FLAC__StreamDecoder *, // Decoder class -DecoderFLAC::DecoderFLAC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) +DecoderFLAC::DecoderFLAC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o, const QString &path) : Decoder(parent, d, i, o) { inited = FALSE; @@ -268,6 +277,14 @@ DecoderFLAC::DecoderFLAC(QObject *parent, DecoderFactory *d, QIODevice *i, Outpu chan = 0; output_size = 0; m_data = 0; + m_path = path; + + m_offset = 0; + m_length = 0; + m_cue = FALSE; + m_data = new flac_data; + m_data->decoder = NULL; + data()->input = i; } @@ -346,10 +363,43 @@ bool DecoderFLAC::initialize() totalTime = 0.0; - if (! input()) + if (!data()->input) { - qWarning("DecoderFLAC: cannot initialize. No input."); - return FALSE; + QString p = m_path; + if (m_path.startsWith("flac://")) //embeded cue track + { + p = QUrl(m_path).path(); + if (!p.endsWith(".flac")) + { + qWarning("DecoderFLAC: invalid url."); + return FALSE; + } + qDebug("DecoderFLAC: using embeded cue"); + TagLib::FLAC::File fileRef(p.toLocal8Bit ()); + //looking for cuesheet comment + TagLib::Ogg::XiphComment *xiph_comment = fileRef.xiphComment(); + QList <FileInfo*> list; + if (xiph_comment && xiph_comment->fieldListMap().contains("CUESHEET")) + { + qDebug("DecoderFLAC: found cuesheet xiph comment"); + CUEParser parser(xiph_comment->fieldListMap()["CUESHEET"].toString().toCString(TRUE), m_path); + int track = m_path.section("#", -1).toInt(); + m_offset = parser.offset(track); + m_length = parser.length(track); + data()->input = new QFile(p); + m_cue = TRUE; + } + else + { + qWarning("DecoderFLAC: unable to find cuesheet comment."); + return FALSE; + } + } + else + { + qWarning("DecoderFLAC: cannot initialize. No input."); + return FALSE; + } } if (! output_buf) @@ -357,9 +407,9 @@ bool DecoderFLAC::initialize() output_at = 0; output_bytes = 0; - if (!input()->isOpen()) + if (!data()->input->isOpen()) { - if (!input()->open(QIODevice::ReadOnly)) + if (!data()->input->open(QIODevice::ReadOnly)) { return FALSE; } @@ -371,19 +421,13 @@ bool DecoderFLAC::initialize() output_at = 0; output_bytes = 0; - if (! input()->isOpen()) + if (! data()->input->isOpen()) { - if (! input()->open(QIODevice::ReadOnly)) + if (! data()->input->open(QIODevice::ReadOnly)) { return FALSE; } } - if (!m_data) - { - m_data = new flac_data; - m_data->decoder = NULL; - } - m_data->bitrate = -1; m_data->abort = 0; m_data->sample_buffer_fill = 0; @@ -424,6 +468,10 @@ bool DecoderFLAC::initialize() totalTime = data()->length; inited = TRUE; + if (m_offset) + seekTime = m_offset; + if (m_length) + totalTime = m_length; qDebug("DecoderFLAC: initialize succes"); return TRUE; } @@ -441,7 +489,7 @@ qint64 DecoderFLAC::lengthInSeconds() void DecoderFLAC::seek(qint64 pos) { if (totalTime > 0) - seekTime = pos; + seekTime = pos + m_offset; } @@ -453,6 +501,11 @@ void DecoderFLAC::deinit() len = freq = bitrate = 0; stat = chan = 0; output_size = 0; + if (!input() && data()->input) + { + data()->input->close(); + data()->input = 0; + }; } void DecoderFLAC::run() @@ -526,6 +579,9 @@ void DecoderFLAC::run() m_finish = TRUE; } + if (m_length && (StateHandler::instance()->elapsed() >= m_length)) + m_finish = TRUE; + mutex()->unlock(); } diff --git a/src/plugins/Input/flac/decoder_flac.h b/src/plugins/Input/flac/decoder_flac.h index 8431a4555..cef0cd561 100644 --- a/src/plugins/Input/flac/decoder_flac.h +++ b/src/plugins/Input/flac/decoder_flac.h @@ -53,12 +53,13 @@ struct flac_data int ok; /* was this stream successfully opened? */ //struct decoder_error error; + QIODevice *input; }; class DecoderFLAC : public Decoder { public: - DecoderFLAC(QObject *, DecoderFactory *, QIODevice *, Output *); + DecoderFLAC(QObject *, DecoderFactory *, QIODevice *, Output *, const QString &path); virtual ~DecoderFLAC(); // Standard Decoder API @@ -98,6 +99,10 @@ private: int chan; unsigned long output_size; double totalTime, seekTime; + QString m_path; + qint64 m_offset; + qint64 m_length; + bool m_cue; }; diff --git a/src/plugins/Input/flac/decoderflacfactory.cpp b/src/plugins/Input/flac/decoderflacfactory.cpp index 1843ccac0..2da4c69de 100644 --- a/src/plugins/Input/flac/decoderflacfactory.cpp +++ b/src/plugins/Input/flac/decoderflacfactory.cpp @@ -21,7 +21,11 @@ #include <QtGui> #include <taglib/tag.h> #include <taglib/fileref.h> +#include <taglib/flacfile.h> +#include <taglib/xiphcomment.h> +#include <taglib/tmap.h> +#include "cueparser.h" #include "detailsdialog.h" #include "decoder_flac.h" #include "decoderflacfactory.h" @@ -48,22 +52,23 @@ const DecoderProperties DecoderFLACFactory::properties() const properties.filter = "*.flac"; properties.description = tr("FLAC Files"); //properties.contentType = ; + properties.protocols = "flac"; properties.hasAbout = TRUE; properties.hasSettings = FALSE; return properties; } Decoder *DecoderFLACFactory::create(QObject *parent, QIODevice *input, - Output *output, const QString &) + Output *output, const QString &path) { - return new DecoderFLAC(parent, this, input, output); + return new DecoderFLAC(parent, this, input, output, path); } QList<FileInfo *> DecoderFLACFactory::createPlayList(const QString &fileName) { FileInfo *info = new FileInfo(fileName); - TagLib::FileRef fileRef(fileName.toLocal8Bit ()); + TagLib::FLAC::File fileRef(fileName.toLocal8Bit ()); TagLib::Tag *tag = fileRef.tag(); if (tag && !tag->isEmpty()) @@ -84,8 +89,19 @@ QList<FileInfo *> DecoderFLACFactory::createPlayList(const QString &fileName) if (fileRef.audioProperties()) info->setLength(fileRef.audioProperties()->length()); + + //looking for cuesheet comment + TagLib::Ogg::XiphComment *xiph_comment = fileRef.xiphComment(); QList <FileInfo*> list; - list << info; + if (xiph_comment && xiph_comment->fieldListMap().contains("CUESHEET")) + { + qDebug(xiph_comment->fieldListMap()["CUESHEET"].toString().toCString(TRUE)); + CUEParser parser(xiph_comment->fieldListMap()["CUESHEET"].toString().toCString(TRUE), fileName); + list = parser.createPlayList(); + delete info; + } + else + list << info; return list; } diff --git a/src/plugins/Input/flac/flac.pro b/src/plugins/Input/flac/flac.pro index 1a24d2fda..8b6d6382b 100644 --- a/src/plugins/Input/flac/flac.pro +++ b/src/plugins/Input/flac/flac.pro @@ -3,12 +3,14 @@ include(../../plugins.pri) FORMS += detailsdialog.ui HEADERS += decoderflacfactory.h \ decoder_flac.h \ - detailsdialog.h + detailsdialog.h \ + cueparser.h SOURCES += decoder_flac.cpp \ decoderflacfactory.cpp \ - detailsdialog.cpp + detailsdialog.cpp \ + cueparser.cpp -TARGET=$$PLUGINS_PREFIX/Input/flac +TARGET =$$PLUGINS_PREFIX/Input/flac QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libflac.so INCLUDEPATH += ../../../ @@ -28,8 +30,8 @@ TRANSLATIONS = translations/flac_plugin_ru.ts \ translations/flac_plugin_de.ts RESOURCES = translations/translations.qrc -isEmpty (LIB_DIR){ -LIB_DIR = /lib +isEmpty(LIB_DIR){ + LIB_DIR = /lib } target.path = $$LIB_DIR/qmmp/Input INSTALLS += target |
