aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--README.RUS2
-rw-r--r--src/plugins/Input/cue/decoder_cue.cpp54
-rw-r--r--src/plugins/Input/cue/decoder_cue.h6
-rw-r--r--src/qmmp/decoder.cpp1
-rw-r--r--src/qmmp/decoder.h2
-rw-r--r--src/qmmp/output.cpp5
-rw-r--r--src/qmmp/output.h1
-rw-r--r--src/qmmp/soundcore.cpp22
-rw-r--r--src/qmmp/soundcore.h10
-rw-r--r--src/qmmp/statehandler.cpp14
-rw-r--r--src/qmmp/statehandler.h12
-rw-r--r--src/qmmpui/mediaplayer.cpp27
-rw-r--r--src/qmmpui/mediaplayer.h3
-rw-r--r--src/qmmpui/playlistmodel.cpp10
-rw-r--r--src/qmmpui/playlistmodel.h2
-rw-r--r--src/qmmpui/playstate.h2
17 files changed, 154 insertions, 21 deletions
diff --git a/README b/README
index e71aca955..4bb518576 100644
--- a/README
+++ b/README
@@ -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.
*/