diff options
| -rw-r--r-- | README | 2 | ||||
| -rw-r--r-- | README.RUS | 2 | ||||
| -rw-r--r-- | src/plugins/Input/cue/decoder_cue.cpp | 54 | ||||
| -rw-r--r-- | src/plugins/Input/cue/decoder_cue.h | 6 | ||||
| -rw-r--r-- | src/qmmp/decoder.cpp | 1 | ||||
| -rw-r--r-- | src/qmmp/decoder.h | 2 | ||||
| -rw-r--r-- | src/qmmp/output.cpp | 5 | ||||
| -rw-r--r-- | src/qmmp/output.h | 1 | ||||
| -rw-r--r-- | src/qmmp/soundcore.cpp | 22 | ||||
| -rw-r--r-- | src/qmmp/soundcore.h | 10 | ||||
| -rw-r--r-- | src/qmmp/statehandler.cpp | 14 | ||||
| -rw-r--r-- | src/qmmp/statehandler.h | 12 | ||||
| -rw-r--r-- | src/qmmpui/mediaplayer.cpp | 27 | ||||
| -rw-r--r-- | src/qmmpui/mediaplayer.h | 3 | ||||
| -rw-r--r-- | src/qmmpui/playlistmodel.cpp | 10 | ||||
| -rw-r--r-- | src/qmmpui/playlistmodel.h | 2 | ||||
| -rw-r--r-- | src/qmmpui/playstate.h | 2 |
17 files changed, 154 insertions, 21 deletions
@@ -61,7 +61,7 @@ Requirements: - cmake >= 2.4.8 (for build only) -Attention! Qmmp build needs lrelease installed. The qt4-linguist package often contains this utility. +Attention! Qmmp build needs lrelease installed. The libqt4-devel package often contains this utility. Configure: cmake ./ diff --git a/README.RUS b/README.RUS index f57a832cf..7e0fa8a0a 100644 --- a/README.RUS +++ b/README.RUS @@ -60,7 +60,7 @@ Qmmp - Qt-based multimedia player - mplayer (Опционально) - cmake >= 2.4.8 (только для сборки) -Внимание! Для сборки Qmmp нужна утилита lrelease. Очень часто она находится в пакете qt4-linguist. +Внимание! Для сборки Qmmp нужна утилита lrelease. Очень часто она находится в пакете libqt4-devel. Конфигурирование: cmake ./ diff --git a/src/plugins/Input/cue/decoder_cue.cpp b/src/plugins/Input/cue/decoder_cue.cpp index ff2761549..f65e17f79 100644 --- a/src/plugins/Input/cue/decoder_cue.cpp +++ b/src/plugins/Input/cue/decoder_cue.cpp @@ -23,6 +23,7 @@ #include <qmmp/recycler.h> #include <qmmp/fileinfo.h> #include <qmmp/decoderfactory.h> +#include <qmmp/soundcore.h> #include <QObject> #include <QFile> @@ -62,6 +63,12 @@ bool DecoderCUE::initialize() } int track = path.section("#", -1).toInt(); path = parser.filePath(track); + // find next track + if(track <= parser.count() - 1) + m_nextUrl = parser.info(track + 1)->path(); + //is it track of another file? + if(QUrl(m_nextUrl).path() != p) + m_nextUrl.clear(); if (!QFile::exists(path)) { @@ -112,6 +119,7 @@ bool DecoderCUE::initialize() //replace default state handler to ignore metadata m_decoder->setStateHandler(new CUEStateHandler(m_decoder)); m_output2->setStateHandler(m_decoder->stateHandler()); + connect(stateHandler(), SIGNAL(aboutToFinish()), SLOT(proccessFinish())); //prepare decoder and ouput objects m_decoder->initialize(); m_decoder->setFragment(m_offset, m_length); @@ -247,19 +255,61 @@ void DecoderCUE::run() m_output2->start(); } +void DecoderCUE::proccessFinish() +{ + qDebug("==>%s", qPrintable(SoundCore::instance()->nextUrl())); + qDebug("==>%s", qPrintable(m_nextUrl)); + if(!SoundCore::instance()->nextUrl().isEmpty() + && !m_nextUrl.isEmpty() + && SoundCore::instance()->nextUrl() == m_nextUrl) + { + qDebug("prefinish"); + qDebug("next url: %s", qPrintable(m_nextUrl)); + int track = m_nextUrl.section("#", -1).toInt(); + QString p = QUrl(m_nextUrl).path(); + p.replace(QString(QUrl::toPercentEncoding("#")), "#"); + p.replace(QString(QUrl::toPercentEncoding("%")), "%"); + + CUEParser parser(p); + m_length = parser.length(track); + m_offset = parser.offset(track); + m_decoder->mutex()->lock(); + m_decoder->setFragment(m_offset, m_length); + m_output2->seek(0); + m_decoder->mutex()->unlock(); + + //stateHandler()->dispatch(Qmmp::Stopped); + //stateHandler()->dispatch(Qmmp::Buffering); + //stateHandler()->dispatch(Qmmp::Playing); + //m_decoder->mutex()->lock(); + //m_output2->seek(0); + //m_decoder->mutex()->unlock(); + + // find next track + if(track <= parser.count() - 1) + m_nextUrl = parser.info(track + 1)->path(); + else + m_nextUrl.clear(); + //is it track of another file? + if(QUrl(m_nextUrl).path() != p) + m_nextUrl.clear(); + emit playbackFinished(); + } + else + Decoder::stop(); +} CUEStateHandler::CUEStateHandler(QObject *parent): StateHandler(parent){} CUEStateHandler::~CUEStateHandler(){} void CUEStateHandler::dispatch(qint64 elapsed, - qint64 totalTime, int bitrate, quint32 frequency, int precision, int channels) { - StateHandler::instance()->dispatch(elapsed, totalTime, bitrate, + StateHandler::instance()->dispatch(elapsed, bitrate, frequency, precision, channels); } diff --git a/src/plugins/Input/cue/decoder_cue.h b/src/plugins/Input/cue/decoder_cue.h index da97735df..a02f82506 100644 --- a/src/plugins/Input/cue/decoder_cue.h +++ b/src/plugins/Input/cue/decoder_cue.h @@ -46,10 +46,13 @@ public: void setEQ(double bands[10], double preamp); void setEQEnabled(bool on); +private slots: + void proccessFinish(); + private: // thread run function void run(); - QString path; + QString path, m_nextUrl; Decoder *m_decoder; Output *m_output2; QIODevice *m_input2; @@ -68,7 +71,6 @@ public: virtual ~CUEStateHandler(); void dispatch(qint64 elapsed, - qint64 totalTime, int bitrate, quint32 frequency, int precision, diff --git a/src/qmmp/decoder.cpp b/src/qmmp/decoder.cpp index d0542b28b..ebbd2528b 100644 --- a/src/qmmp/decoder.cpp +++ b/src/qmmp/decoder.cpp @@ -188,6 +188,7 @@ void Decoder::setFragment(qint64 offset, qint64 length) _m_offset_in_bytes = offset * _m_freq * _m_bps * _m_chan / 8000; _m_length_in_bytes = length * _m_freq * _m_bps * _m_chan / 8000; _m_offset = offset; + _m_totalBytes = 0; } void Decoder::stop() diff --git a/src/qmmp/decoder.h b/src/qmmp/decoder.h index f1d829bf8..a0a311da9 100644 --- a/src/qmmp/decoder.h +++ b/src/qmmp/decoder.h @@ -182,6 +182,8 @@ public: */ static bool isEnabled(DecoderFactory* factory); + bool isFinished(){return _m_user_stop;} + signals: /*! * Emitted when the decoder has finished playback. diff --git a/src/qmmp/output.cpp b/src/qmmp/output.cpp index 0d26cf03d..39daf4f8b 100644 --- a/src/qmmp/output.cpp +++ b/src/qmmp/output.cpp @@ -125,14 +125,13 @@ void Output::clearVisuals() } void Output::dispatch(qint64 elapsed, - qint64 totalTime, int bitrate, int frequency, int precision, int channels) { if (m_handler) - m_handler->dispatch(elapsed, totalTime, bitrate, frequency, precision, channels); + m_handler->dispatch(elapsed, bitrate, frequency, precision, channels); } void Output::dispatch(const Qmmp::State &state) @@ -233,7 +232,7 @@ void Output::status() if (ct > m_currentMilliseconds) { m_currentMilliseconds = ct; - dispatch(m_currentMilliseconds, m_totalWritten, m_kbps, + dispatch(m_currentMilliseconds, m_kbps, m_frequency, m_precision, m_channels); } } diff --git a/src/qmmp/output.h b/src/qmmp/output.h index ecbd5b740..5b864a7b0 100644 --- a/src/qmmp/output.h +++ b/src/qmmp/output.h @@ -145,7 +145,6 @@ private: void status(); void changeVolume(uchar *data, qint64 size, int chan); void dispatch(qint64 elapsed, - qint64 totalTime, int bitrate, int frequency, int precision, diff --git a/src/qmmp/soundcore.cpp b/src/qmmp/soundcore.cpp index c0e9e3582..e335d1580 100644 --- a/src/qmmp/soundcore.cpp +++ b/src/qmmp/soundcore.cpp @@ -53,8 +53,6 @@ SoundCore::SoundCore(QObject *parent) for (int i = 1; i < 10; ++i) m_bands[i] = 0; m_handler = new StateHandler(this); - //StateHandler::instance() = 0; - //StateHandler::m_instance = m_handler; connect(m_handler, SIGNAL(elapsedChanged(qint64)), SIGNAL(elapsedChanged(qint64))); connect(m_handler, SIGNAL(bitrateChanged(int)), SIGNAL(bitrateChanged(int))); connect(m_handler, SIGNAL(frequencyChanged(quint32)), SIGNAL(frequencyChanged(quint32))); @@ -62,6 +60,7 @@ SoundCore::SoundCore(QObject *parent) connect(m_handler, SIGNAL(channelsChanged(int)), SIGNAL(channelsChanged(int))); connect(m_handler, SIGNAL(metaDataChanged ()), SIGNAL(metaDataChanged ())); connect(m_handler, SIGNAL(stateChanged (Qmmp::State)), SIGNAL(stateChanged(Qmmp::State))); + connect(m_handler, SIGNAL(aboutToFinish()), SIGNAL(aboutToFinish())); m_volumeControl = VolumeControl::create(this); connect(m_volumeControl, SIGNAL(volumeChanged(int, int)), SIGNAL(volumeChanged(int, int))); } @@ -74,7 +73,20 @@ SoundCore::~SoundCore() bool SoundCore::play(const QString &source) { + qDebug("==%s", qPrintable(source)); + qDebug("==%s",qPrintable(m_next)); + if((m_next == source) && m_decoder && !m_decoder->isFinished() + && m_decoder->factory()->properties().noOutput) + { + m_handler->dispatch(Qmmp::Stopped); + m_handler->dispatch(Qmmp::Playing); + m_handler->dispatch(Qmmp::Buffering); + m_handler->dispatch(Qmmp::Playing); + m_source = source; + return TRUE; + } stop(); + m_source = source; if (m_handler->state() != Qmmp::Stopped) //clear error state m_handler->dispatch(Qmmp::Stopped); @@ -133,9 +145,15 @@ bool SoundCore::play(const QString &source) return FALSE; } +void SoundCore::setNext(const QString &source) +{ + m_next = source; +} + void SoundCore::stop() { m_factory = 0; + //m_next.clear(); m_source.clear(); if (m_decoder /*&& m_decoder->isRunning()*/) { diff --git a/src/qmmp/soundcore.h b/src/qmmp/soundcore.h index c07ad332e..b39fab1d2 100644 --- a/src/qmmp/soundcore.h +++ b/src/qmmp/soundcore.h @@ -127,6 +127,8 @@ public slots: * otherwise returns \b false. Useful for invalid files skipping. */ bool play(const QString &source); + + void setNext(const QString &source); /*! * Stops playback */ @@ -144,6 +146,8 @@ public slots: */ const QString url(); + const QString nextUrl(){return m_next;} + signals: /*! * This signal is emitted when the stream reader fills it's buffer. @@ -193,6 +197,11 @@ signals: * @param right Right channel volume level. It should be \b [0..100] */ void volumeChanged(int left, int right); + /*! + * Emitted before the playback ends. Use this signal to tell decoder about next track. + * This may be useful for multitrack formats like CDA or cue sheets. + */ + void aboutToFinish(); private slots: bool decode(); @@ -201,6 +210,7 @@ private: Decoder* m_decoder; DecoderFactory* m_factory; //QUrl m_url; + QString m_next; QString m_source; Output* m_output; QIODevice* m_input; diff --git a/src/qmmp/statehandler.cpp b/src/qmmp/statehandler.cpp index 4e08e86a0..1086e477b 100644 --- a/src/qmmp/statehandler.cpp +++ b/src/qmmp/statehandler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Ilya Kotov * + * Copyright (C) 2008-2009 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -24,6 +24,7 @@ #include "statehandler.h" #define TICK_INTERVAL 250 +#define PREFINISH_TIME 5000 StateHandler* StateHandler::m_instance = 0; @@ -39,6 +40,7 @@ StateHandler::StateHandler(QObject *parent) m_precision = 0; m_channels = 0; m_sendMeta = FALSE; + m_sendAboutToFinish = TRUE; m_state = Qmmp::Stopped; } @@ -51,13 +53,11 @@ StateHandler::~StateHandler() void StateHandler::dispatch(qint64 elapsed, - qint64 totalTime, int bitrate, quint32 frequency, int precision, int channels) { - Q_UNUSED(totalTime); m_mutex.lock(); if (qAbs(m_elapsed - elapsed) > TICK_INTERVAL) { @@ -68,6 +68,13 @@ void StateHandler::dispatch(qint64 elapsed, m_bitrate = bitrate; emit (bitrateChanged(bitrate)); } + if((SoundCore::instance()->totalTime() > PREFINISH_TIME) + && (SoundCore::instance()->totalTime() - m_elapsed < PREFINISH_TIME) + && m_sendAboutToFinish) + { + m_sendAboutToFinish = FALSE; + emit aboutToFinish(); + } } if (m_frequency != frequency) { @@ -128,6 +135,7 @@ void StateHandler::dispatch(const Qmmp::State &state) m_precision = 0; m_channels = 0; m_sendMeta = FALSE; + m_sendAboutToFinish = TRUE; m_metaData.clear(); } if (m_state != state) diff --git a/src/qmmp/statehandler.h b/src/qmmp/statehandler.h index d61ad5cb0..0e64b0a7e 100644 --- a/src/qmmp/statehandler.h +++ b/src/qmmp/statehandler.h @@ -45,14 +45,12 @@ public: /*! * Sends information about playback progress. * @param elapsed Current time (in milliseconds). - * @param totalTime Total track length (in milliseconds). * @param bitrate Current bitrate (in kbps). * @param frequency Current samplerate (in Hz). * @param precision Sample size (in bits). * @param channels Number of channels. */ virtual void dispatch(qint64 elapsed, - qint64 totalTime, int bitrate, quint32 frequency, int precision, @@ -131,19 +129,25 @@ signals: /*! * Emitted when new metadata is available. */ - void metaDataChanged (); + void metaDataChanged(); /*! * This signal is emitted when the playback state has changed. */ - void stateChanged (Qmmp::State newState); + void stateChanged(Qmmp::State newState); /*! * Emitted when playback has finished. */ void finished(); + /*! + * Emitted before the playback ends. Use this signal to tell decoder about next track. + * This may be useful for multitrack formats like CDA or cue sheets. + */ + void aboutToFinish(); private: qint64 m_elapsed; quint32 m_frequency; + bool m_sendAboutToFinish; int m_bitrate, m_precision, m_channels; static StateHandler* m_instance; QMap <Qmmp::MetaData, QString> m_metaData; diff --git a/src/qmmpui/mediaplayer.cpp b/src/qmmpui/mediaplayer.cpp index 0f6b7a79f..3f7545091 100644 --- a/src/qmmpui/mediaplayer.cpp +++ b/src/qmmpui/mediaplayer.cpp @@ -64,6 +64,7 @@ void MediaPlayer::initialize(SoundCore *core, PlayListModel *model) m_model = model; m_repeat = FALSE; connect(m_core, SIGNAL(finished()), SLOT(next())); + connect(m_core, SIGNAL(aboutToFinish()), SLOT(sendNext())); } PlayListModel *MediaPlayer::playListModel() @@ -78,7 +79,9 @@ bool MediaPlayer::isRepeatable() const void MediaPlayer::play() { + qDebug("+1"); m_model->doCurrentVisibleRequest(); + qDebug("+2"); if (m_core->state() == Qmmp::Paused) { m_core->pause(); @@ -87,12 +90,15 @@ void MediaPlayer::play() if (m_model->count() == 0) return; - + qDebug("+3"); QString s = m_model->currentItem()->url(); if (s.isEmpty()) return; + qDebug("+4"); + //m_core->setNext (m_model->nextItem()->url()); if (!m_core->play(s)) { + qDebug("+5"); //find out the reason why playback failed switch ((int) m_core->state()) { @@ -127,7 +133,19 @@ void MediaPlayer::play() } } else + { + qDebug("1"); m_skips = 0; + qDebug("2"); + if(m_model->nextItem()) + { + qDebug("3"); + m_core->setNext (m_model->nextItem()->url()); + qDebug("MediaPlayer: current item: %s",qPrintable(m_core->url())); + qDebug("MediaPlayer: next item: %s",qPrintable(m_core->nextUrl())); + qDebug("4"); + } + } } void MediaPlayer::stop() @@ -190,3 +208,10 @@ void MediaPlayer::setRepeatable(bool r) m_repeat = r; emit repeatableChanged(r); } + +void MediaPlayer::sendNext() +{ + /*if(m_model->nextItem()) + qDebug("MediaPlayer: next item: %s",qPrintable(m_model->nextItem()->url()));*/ +} + diff --git a/src/qmmpui/mediaplayer.h b/src/qmmpui/mediaplayer.h index 7c03bf3a3..c4f5d5bdd 100644 --- a/src/qmmpui/mediaplayer.h +++ b/src/qmmpui/mediaplayer.h @@ -90,6 +90,9 @@ signals: */ void repeatableChanged(bool enabled); +private slots: + void sendNext(); + private: PlayListModel *m_model; SoundCore *m_core; diff --git a/src/qmmpui/playlistmodel.cpp b/src/qmmpui/playlistmodel.cpp index 2e22cf479..eb2aeb705 100644 --- a/src/qmmpui/playlistmodel.cpp +++ b/src/qmmpui/playlistmodel.cpp @@ -128,6 +128,16 @@ PlayListItem* PlayListModel::currentItem() return m_items.at(qMin(m_items.size() - 1, m_current)); } +PlayListItem* PlayListModel::nextItem() +{ + qDebug("==== %d =====", m_current +1); + if(isShuffle() || m_items.isEmpty()) + return 0; + if(m_current < m_items.size() - 1) + return m_items.at(m_current + 1); + return 0; +} + PlayListItem* PlayListModel::item(int row) const { return (row < m_items.size() && row >= 0) ? m_items.at(row) : 0; diff --git a/src/qmmpui/playlistmodel.h b/src/qmmpui/playlistmodel.h index 10a7293c0..b8a095a69 100644 --- a/src/qmmpui/playlistmodel.h +++ b/src/qmmpui/playlistmodel.h @@ -115,6 +115,8 @@ public: * Returns the current item. */ PlayListItem* currentItem(); + + PlayListItem* nextItem(); /*! * Returns the row of the \b item */ diff --git a/src/qmmpui/playstate.h b/src/qmmpui/playstate.h index a61ea3d66..c03ac268e 100644 --- a/src/qmmpui/playstate.h +++ b/src/qmmpui/playstate.h @@ -46,7 +46,7 @@ public: virtual void resetState() { ; - }; + } /*! * Service method, can be used for state initializing. */ |
