aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2009-09-12 14:33:49 +0000
committertrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2009-09-12 14:33:49 +0000
commit8fbf582b60b6267463e6a3dc289b52e49fdc2f83 (patch)
tree9540a7b90f2b52ccb34104f2f89d4c1e22445a70 /src
parentc89ba4ee3ccca9a2f4b38d4b76ca666f94babbd4 (diff)
downloadqmmp-8fbf582b60b6267463e6a3dc289b52e49fdc2f83.tar.gz
qmmp-8fbf582b60b6267463e6a3dc289b52e49fdc2f83.tar.bz2
qmmp-8fbf582b60b6267463e6a3dc289b52e49fdc2f83.zip
prepare for gapless playback support
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@1207 90c681e8-e032-0410-971d-27865f9a5e38
Diffstat (limited to 'src')
-rw-r--r--src/plugins/Input/mad/decoder_mad.cpp39
-rw-r--r--src/plugins/Input/mad/decoder_mad.h9
-rw-r--r--src/plugins/Input/mad/decodermadfactory.cpp4
-rw-r--r--src/plugins/Input/mad/decodermadfactory.h2
-rw-r--r--src/qmmp/CMakeLists.txt6
-rw-r--r--src/qmmp/abstractengine.cpp35
-rw-r--r--src/qmmp/abstractengine.h124
-rw-r--r--src/qmmp/audioparameters.cpp54
-rw-r--r--src/qmmp/audioparameters.h27
-rw-r--r--src/qmmp/decoder.cpp374
-rw-r--r--src/qmmp/decoder.h182
-rw-r--r--src/qmmp/decoderfactory.h5
-rw-r--r--src/qmmp/output.cpp3
-rw-r--r--src/qmmp/qmmp.pro13
-rw-r--r--src/qmmp/qmmpaudioengine.cpp484
-rw-r--r--src/qmmp/qmmpaudioengine.h89
-rw-r--r--src/qmmp/soundcore.cpp202
-rw-r--r--src/qmmp/soundcore.h7
-rw-r--r--src/qmmpui/mediaplayer.cpp29
-rw-r--r--src/qmmpui/mediaplayer.h1
20 files changed, 944 insertions, 745 deletions
diff --git a/src/plugins/Input/mad/decoder_mad.cpp b/src/plugins/Input/mad/decoder_mad.cpp
index 572063ba2..bbfd43251 100644
--- a/src/plugins/Input/mad/decoder_mad.cpp
+++ b/src/plugins/Input/mad/decoder_mad.cpp
@@ -21,8 +21,8 @@
#define INPUT_BUFFER_SIZE (32*1024)
-DecoderMAD::DecoderMAD(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
- : Decoder(parent, d, i, o)
+DecoderMAD::DecoderMAD(QIODevice *i)
+ : Decoder()
{
m_inited = false;
m_totalTime = 0.;
@@ -36,6 +36,7 @@ DecoderMAD::DecoderMAD(QObject *parent, DecoderFactory *d, QIODevice *i, Output
m_output_at = 0;
m_skip_frames = 0;
m_eof = false;
+ m_input = i;
}
DecoderMAD::~DecoderMAD()
@@ -62,7 +63,7 @@ bool DecoderMAD::initialize()
m_output_bytes = 0;
m_output_at = 0;
- if (!input())
+ if (!m_input)
{
qWarning("DecoderMAD: cannot initialize. No input.");
return FALSE;
@@ -71,20 +72,20 @@ bool DecoderMAD::initialize()
if (!m_input_buf)
m_input_buf = new char[INPUT_BUFFER_SIZE];
- if (!input()->isOpen())
+ if (!m_input->isOpen())
{
- if (!input()->open(QIODevice::ReadOnly))
+ if (!m_input->open(QIODevice::ReadOnly))
{
- qWarning("DecoderMAD: %s", qPrintable(input()->errorString ()));
+ qWarning("DecoderMAD: %s", qPrintable(m_input->errorString ()));
return FALSE;
}
}
- if (input()->isSequential ()) //for streams only
+ if (m_input->isSequential ()) //for streams only
{
- TagExtractor extractor(input());
+ TagExtractor extractor(m_input);
if(!extractor.id3v2tag().isEmpty())
- stateHandler()->dispatch(extractor.id3v2tag());
+ StateHandler::instance()->dispatch(extractor.id3v2tag());
}
mad_stream_init(&stream);
@@ -211,7 +212,7 @@ bool DecoderMAD::findHeader()
memmove (m_input_buf, stream.next_frame, remaining);
}
- m_input_bytes = input()->read(m_input_buf + remaining, INPUT_BUFFER_SIZE - remaining);
+ m_input_bytes = m_input->read(m_input_buf + remaining, INPUT_BUFFER_SIZE - remaining);
if (m_input_bytes <= 0)
break;
@@ -234,7 +235,7 @@ bool DecoderMAD::findHeader()
}
result = TRUE;
- if (input()->isSequential())
+ if (m_input->isSequential())
break;
count ++;
@@ -279,9 +280,9 @@ bool DecoderMAD::findHeader()
if (!result)
return FALSE;
- if (!is_vbr && !input()->isSequential())
+ if (!is_vbr && !m_input->isSequential())
{
- double time = (input()->size() * 8.0) / (header.bitrate);
+ double time = (m_input->size() * 8.0) / (header.bitrate);
double timefrac = (double)time - ((long)(time));
mad_timer_set(&duration, (long)time, (long)(timefrac*100), 100);
}
@@ -297,7 +298,7 @@ bool DecoderMAD::findHeader()
m_channels = MAD_NCHANNELS(&header);
m_bitrate = header.bitrate / 1000;
mad_header_finish(&header);
- input()->seek(0);
+ m_input->seek(0);
m_input_bytes = 0;
return TRUE;
}
@@ -314,7 +315,7 @@ int DecoderMAD::bitrate()
return int(m_bitrate);
}
-qint64 DecoderMAD::readAudio(char *data, qint64 size)
+qint64 DecoderMAD::read(char *data, qint64 size)
{
forever
{
@@ -358,12 +359,12 @@ qint64 DecoderMAD::readAudio(char *data, qint64 size)
return madOutput(data, size);
}
}
-void DecoderMAD::seekAudio(qint64 pos)
+void DecoderMAD::seek(qint64 pos)
{
if(m_totalTime > 0)
{
- qint64 seek_pos = qint64(pos * input()->size() / m_totalTime);
- input()->seek(seek_pos);
+ qint64 seek_pos = qint64(pos * m_input->size() / m_totalTime);
+ m_input->seek(seek_pos);
mad_frame_mute(&frame);
mad_synth_mute(&synth);
stream.error = MAD_ERROR_BUFLEN;
@@ -381,7 +382,7 @@ bool DecoderMAD::fillBuffer()
m_input_bytes = &m_input_buf[m_input_bytes] - (char *) stream.next_frame;
memmove(m_input_buf, stream.next_frame, m_input_bytes);
}
- int len = input()->read((char *) m_input_buf + m_input_bytes, INPUT_BUFFER_SIZE - m_input_bytes);
+ int len = m_input->read((char *) m_input_buf + m_input_bytes, INPUT_BUFFER_SIZE - m_input_bytes);
if (!len)
{
qDebug("DecoderMAD: end of file");
diff --git a/src/plugins/Input/mad/decoder_mad.h b/src/plugins/Input/mad/decoder_mad.h
index 8e66ac0c4..86369b4d9 100644
--- a/src/plugins/Input/mad/decoder_mad.h
+++ b/src/plugins/Input/mad/decoder_mad.h
@@ -7,6 +7,7 @@
#ifndef DECODER_MAD_H
#define DECODER_MAD_H
+class QIODevice;
class DecoderMAD;
#include <qmmp/decoder.h>
@@ -21,8 +22,7 @@ extern "C"
class DecoderMAD : public Decoder
{
public:
- DecoderMAD(QObject *parent = 0, DecoderFactory *d = 0,
- QIODevice *i = 0, Output *o = 0);
+ DecoderMAD(QIODevice *i);
virtual ~DecoderMAD();
// standard decoder API
@@ -31,8 +31,8 @@ public:
int bitrate();
private:
- qint64 readAudio(char *data, qint64 size);
- void seekAudio(qint64);
+ qint64 read(char *data, qint64 size);
+ void seek(qint64);
// helper functions
qint64 madOutput(char *data, qint64 size);
@@ -47,6 +47,7 @@ private:
uint m_bitrate;
long m_freq, m_len;
qint64 m_output_bytes, m_output_at;
+ QIODevice *m_input;
// file input buffer
char *m_input_buf;
diff --git a/src/plugins/Input/mad/decodermadfactory.cpp b/src/plugins/Input/mad/decodermadfactory.cpp
index d4d881cd8..47de2e828 100644
--- a/src/plugins/Input/mad/decodermadfactory.cpp
+++ b/src/plugins/Input/mad/decodermadfactory.cpp
@@ -94,9 +94,9 @@ const DecoderProperties DecoderMADFactory::properties() const
return properties;
}
-Decoder *DecoderMADFactory::create(QObject *parent, QIODevice *input, Output *output, const QString &)
+Decoder *DecoderMADFactory::create(QIODevice *input, const QString &)
{
- return new DecoderMAD(parent, this, input, output);
+ return new DecoderMAD(input);
}
QList<FileInfo *> DecoderMADFactory::createPlayList(const QString &fileName, bool useMetaData)
diff --git a/src/plugins/Input/mad/decodermadfactory.h b/src/plugins/Input/mad/decodermadfactory.h
index bd657514c..6107e56f0 100644
--- a/src/plugins/Input/mad/decodermadfactory.h
+++ b/src/plugins/Input/mad/decodermadfactory.h
@@ -43,7 +43,7 @@ public:
bool supports(const QString &source) const;
bool canDecode(QIODevice *input) const;
const DecoderProperties properties() const;
- Decoder *create(QObject *, QIODevice *, Output *, const QString &);
+ Decoder *create(QIODevice *, const QString &);
QList<FileInfo *> createPlayList(const QString &fileName, bool useMetaData);
MetaDataModel* createMetaDataModel(const QString &path, QObject *parent = 0);
void showSettings(QWidget *parent);
diff --git a/src/qmmp/CMakeLists.txt b/src/qmmp/CMakeLists.txt
index 6cee8ee22..b3d6904af 100644
--- a/src/qmmp/CMakeLists.txt
+++ b/src/qmmp/CMakeLists.txt
@@ -54,6 +54,9 @@ SET(libqmmp_SRCS
volumecontrol.cpp
metadatamodel.cpp
tagmodel.cpp
+ qmmpaudioengine.cpp
+ abstractengine.cpp
+ audioparameters.cpp
)
SET(libqmmp_MOC_HDRS
@@ -78,6 +81,9 @@ SET(libqmmp_MOC_HDRS
volumecontrol.h
metadatamodel.h
tagmodel.h
+ qmmpaudioengine.h
+ abstractengine.h
+ audioparameters.h
)
SET(libqmmp_DEVEL_HDRS
diff --git a/src/qmmp/abstractengine.cpp b/src/qmmp/abstractengine.cpp
new file mode 100644
index 000000000..b92c3e069
--- /dev/null
+++ b/src/qmmp/abstractengine.cpp
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * Copyright (C) 2009 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 "abstractengine.h"
+
+AbstractEngine::AbstractEngine(QObject *parent) : QThread(parent)
+{
+}
+
+QMutex *AbstractEngine::mutex()
+{
+ return &m_mutex;
+}
+
+QWaitCondition *AbstractEngine::cond()
+{
+ return &m_waitCondition;
+}
diff --git a/src/qmmp/abstractengine.h b/src/qmmp/abstractengine.h
new file mode 100644
index 000000000..eed9f9cc3
--- /dev/null
+++ b/src/qmmp/abstractengine.h
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * Copyright (C) 2009 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 ABSTRACTENGINE_H
+#define ABSTRACTENGINE_H
+
+
+#include <QMutex>
+#include <QWaitCondition>
+#include <QThread>
+
+class QIODevice;
+
+/*!
+ * @author Ilya Kotov <forkotov02@hotmail.ru>
+ */
+
+class AbstractEngine : public QThread
+{
+ Q_OBJECT
+public:
+ AbstractEngine(QObject *parent = 0);
+
+ /*!
+ * Prepares decoder for usage.
+ * Subclass should reimplement this function.
+ */
+ virtual bool initialize(QIODevice *input, const QString &source) = 0;
+ /*!
+ * Returns the total time in milliseconds.
+ * Subclass should reimplement this function.
+ */
+ virtual qint64 totalTime() = 0;
+ /*!
+ * Requests a seek to the time \b time indicated, specified in milliseconds.
+ */
+ virtual void seek(qint64 time) = 0;
+ /*!
+ * Requests playback to stop
+ */
+ virtual void stop() = 0;
+ /*!
+ * Returns current bitrate (in kbps).
+ * Subclass should reimplement this function.
+ */
+ //virtual int bitrate();
+ /*!
+ * Requests playback to pause. If it was paused already, playback should resume.
+ * Subclass with own output should reimplement this function.
+ */
+ //virtual void pause();
+ /*!
+ * Returns decoder input or 0 if input is not specified.
+ */
+ //QIODevice *input();
+ /*!
+ * Returns decoder output or 0 if output is not specified.
+ */
+ //Output *output();
+ /*!
+ * Returns mutex pointer.
+ */
+ QMutex *mutex();
+ /*!
+ * Returns wait condition pointer.
+ */
+ QWaitCondition *cond();
+ /*!
+ * Sets equalizer settings. Each item of \p bands[] and \p reamp should be \b -20.0..20.0
+ * Subclass with own equalizer should reimplement this function.
+ */
+ //virtual void setEQ(double bands[10], double preamp);
+ /*!
+ * Enables equalizer if \p on is \b true or disables it if \p on is \b false
+ * Subclass with own equalizer should reimplement this function.
+ */
+ //virtual void setEQEnabled(bool on);
+ /*!
+ * Returns \b true if \b file is supported by input plugins, otherwise returns \b false
+ */
+ //bool supports(const QString &file);
+
+signals:
+ /*!
+ * Emitted when the decoder has finished playback.
+ */
+ void playbackFinished();
+
+protected:
+ /*!
+ * The starting point for the decoding thread.
+ */
+ virtual void run() = 0;
+
+protected slots:
+ /*!
+ * Subclass should call this slot when decoding is finished.
+ */
+ //void finish();
+
+private:
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+};
+
+
+#endif // ABSTRACTENGINE_H
diff --git a/src/qmmp/audioparameters.cpp b/src/qmmp/audioparameters.cpp
new file mode 100644
index 000000000..0a858a1e4
--- /dev/null
+++ b/src/qmmp/audioparameters.cpp
@@ -0,0 +1,54 @@
+#include "audioparameters.h"
+
+AudioParameters::AudioParameters()
+{
+ m_srate = 0;
+ m_chan = 0;
+ m_bps = 0;
+}
+
+AudioParameters::AudioParameters(const AudioParameters &other)
+{
+ m_srate = other.sampleRate();
+ m_chan = other.channels();
+ m_bps = other.bits();
+}
+
+AudioParameters::AudioParameters(quint32 srate, int chan, int bps)
+{
+ m_srate = srate;
+ m_chan = chan;
+ m_bps = bps;
+}
+
+void AudioParameters::operator=(const AudioParameters &p)
+{
+ m_srate = p.sampleRate();
+ m_chan = p.channels();
+ m_bps = p.bits();
+}
+
+bool AudioParameters::operator==(const AudioParameters &p) const
+{
+ return m_srate == p.sampleRate() && m_chan == p.channels() && m_bps == p.bits();
+}
+
+bool AudioParameters::operator!=(const AudioParameters &p) const
+{
+ return !operator!=(p);
+}
+
+quint32 AudioParameters::sampleRate() const
+{
+ return m_srate;
+}
+
+int AudioParameters::channels() const
+{
+ return m_chan;
+}
+
+int AudioParameters::bits() const
+{
+ return m_bps;
+}
diff --git a/src/qmmp/audioparameters.h b/src/qmmp/audioparameters.h
new file mode 100644
index 000000000..d7db83355
--- /dev/null
+++ b/src/qmmp/audioparameters.h
@@ -0,0 +1,27 @@
+#ifndef AUDIOPARAMETERS_H
+#define AUDIOPARAMETERS_H
+
+#include <QtGlobal>
+
+class AudioParameters
+{
+public:
+ AudioParameters();
+ AudioParameters(quint32 srate, int chan, int bps);
+ AudioParameters(const AudioParameters &other);
+
+ void operator=(const AudioParameters &p);
+ bool operator==(const AudioParameters &p) const;
+ bool operator!=(const AudioParameters &p) const;
+
+ quint32 sampleRate() const;
+ int channels() const;
+ int bits() const;
+
+private:
+ quint32 m_srate;
+ int m_chan;
+ int m_bps;
+};
+
+#endif // AUDIOPARAMETERS_H
diff --git a/src/qmmp/decoder.cpp b/src/qmmp/decoder.cpp
index 59439c7f0..68e966dba 100644
--- a/src/qmmp/decoder.cpp
+++ b/src/qmmp/decoder.cpp
@@ -26,383 +26,17 @@ extern "C"
}
#include "decoder.h"
-
-Decoder::Decoder(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
- : QThread(parent), _m_factory(d), _m_input(i), _m_output(o), _m_eqInited(FALSE),
- _m_useEQ(FALSE)
-{
- init();
-}
-
-Decoder::Decoder(QObject *parent, DecoderFactory *d, Output *o)
- : QThread(parent), _m_factory(d), _m_input(0), _m_output(o), _m_eqInited(FALSE),
- _m_useEQ(FALSE)
-{
- init();
-}
-
Decoder::~Decoder()
-{
- _m_factory = 0;
- _m_input = 0;
- _m_output = 0;
- _blksize = 0;
- _m_handler = 0;
- _m_done = FALSE;
- _m_finish = FALSE;
- _m_totalTime = 0;
- _m_seekTime = -1;
- _m_output_at = 0;
- _m_user_stop = FALSE;
- _m_bks = Buffer::size();
- _m_output_buf = 0;
- _m_bitrate = 0;
- _m_chan = 0;
- _m_freq = 0;
- _m_bps = 0;
- _m_offset_in_bytes = 0;
- _m_length_in_bytes = 0;
- _m_totalBytes = 0;
- _m_offset = 0;
- if(_m_output_buf)
- delete [] _m_output_buf;
- _m_output_buf = 0;
-}
-
-void Decoder::init()
-{
- if (_m_output)
- _m_output->recycler()->clear();
- double b[] = {0,0,0,0,0,0,0,0,0,0};
- setEQ(b, 0);
- qRegisterMetaType<Qmmp::State>("Qmmp::State");
- _blksize = Buffer::size();
- _m_effects = Effect::create(this);
- _m_handler = 0;
- _m_done = FALSE;
- _m_finish = FALSE;
- _m_totalTime = 0;
- _m_seekTime = -1;
- _m_output_at = 0;
- _m_user_stop = FALSE;
- _m_bks = Buffer::size();
- _m_output_buf = 0;
- _m_bitrate = 0;
- _m_chan = 0;
- _m_freq = 0;
- _m_bps = 0;
- _m_offset_in_bytes = 0;
- _m_length_in_bytes = 0;
- _m_offset = 0;
- _m_totalBytes = 0;
- _m_useNextUrl = FALSE;
-}
-
-DecoderFactory *Decoder::factory() const
-{
- return _m_factory;
-}
-
-QIODevice *Decoder::input()
-{
- return _m_input;
-}
-
-Output *Decoder::output()
-{
- return _m_output;
-}
-
-QMutex *Decoder::mutex()
-{
- return &m_mutex;
-}
-
-QWaitCondition *Decoder::cond()
-{
- return &m_waitCondition;
-}
-
-StateHandler *Decoder::stateHandler()
-{
- return _m_handler;
-}
-
-void Decoder::setStateHandler(StateHandler *handler)
-{
- _m_handler = handler;
-}
-
-void Decoder::setEQ(double bands[10], double preamp)
-{
- set_preamp(0, 1.0 + 0.0932471 *preamp + 0.00279033 * preamp * preamp);
- set_preamp(1, 1.0 + 0.0932471 *preamp + 0.00279033 * preamp * preamp);
- for (int i=0; i<10; ++i)
- {
- double value = bands[i];
- set_gain(i,0, 0.03*value+0.000999999*value*value);
- set_gain(i,1, 0.03*value+0.000999999*value*value);
- }
-}
-
-void Decoder::setEQEnabled(bool on)
-{
- _m_useEQ = on;
-}
+{}
void Decoder::configure(quint32 srate, int chan, int bps)
{
- Effect* effect = 0;
- _m_freq = srate;
- _m_chan = chan;
- _m_bps = bps;
- foreach(effect, _m_effects)
- {
- effect->configure(srate, chan, bps);
- srate = effect->sampleRate();
- chan = effect->channels();
- bps = effect->bitsPerSample();
- }
- _m_chan = chan;
- if (_m_output)
- {
- _m_output->configure(srate, chan, bps);
- if (!_m_output_buf)
- _m_output_buf = new unsigned char[Qmmp::globalBufferSize()];
- }
-}
-
-void Decoder::seek(qint64 time)
-{
- _m_seekTime = time;
-}
-
-int Decoder::bitrate()
-{
- return 0;
-}
-
-void Decoder::pause(){}
-
-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()
-{
- _m_nextUrl.clear();
- _m_user_stop = TRUE;
-}
-
-void Decoder::setNextUrl(const QString &url)
-{
- _m_nextUrl = url;
-}
-
-void Decoder::clearNextUrl()
-{
- _m_nextUrl.clear();
- _m_useNextUrl = FALSE;
-}
-
-bool Decoder::nextUrlAccepted()
-{
- return _m_useNextUrl;
-}
-
-qint64 Decoder::produceSound(char *data, qint64 size, quint32 brate, int chan)
-{
- ulong sz = size < _blksize ? size : _blksize;
-
- if (_m_useEQ)
- {
- if (!_m_eqInited)
- {
- init_iir();
- _m_eqInited = TRUE;
- }
- iir((void*) data, sz, chan);
- }
- char *out_data = data;
- char *prev_data = data;
- qint64 w = sz;
- Effect* effect = 0;
- foreach(effect, _m_effects)
- {
- w = effect->process(prev_data, sz, &out_data);
-
- if (w <= 0)
- {
- // copy data if plugin can not process it
- w = sz;
- out_data = new char[w];
- memcpy(out_data, prev_data, w);
- }
- if (data != prev_data)
- delete prev_data;
- prev_data = out_data;
- }
-
- Buffer *b = output()->recycler()->get(w);
-
- memcpy(b->data, out_data, w);
-
- if (data != out_data)
- delete out_data;
-
- if (w < _blksize + b->exceeding)
- memset(b->data + w, 0, _blksize + b->exceeding - w);
-
- b->nbytes = w;
- b->rate = brate;
-
- output()->recycler()->add();
-
- size -= sz;
- memmove(data, data + sz, size);
- return sz;
-}
-
-void Decoder::finish()
-{
- if (output())
- {
- output()->mutex()->lock ();
- output()->finish();
- output()->mutex()->unlock();
- }
- emit playbackFinished();
-}
-
-qint64 Decoder::readAudio(char*, qint64)
-{
- return 0;
-}
-
-void Decoder::seekAudio(qint64){}
-
-void Decoder::run()
-{
- Q_ASSERT(_m_chan == 0);
- Q_ASSERT(!_m_output_buf);
- mutex()->lock ();
-
- qint64 len = 0;
- if(_m_offset > 0)
- seekAudio(_m_offset);
-
- mutex()->unlock();
-
- while (! _m_done && ! _m_finish)
- {
- mutex()->lock ();
- // decode
-
- if (_m_seekTime >= 0)
- {
- seekAudio(_m_seekTime + _m_offset);
- _m_totalBytes = _m_seekTime * _m_freq * _m_bps * _m_chan / 8000;
- _m_seekTime = -1;
- output()->recycler()->mutex()->lock ();
- while(output()->recycler()->used() > 1)
- output()->recycler()->done();
- output()->recycler()->mutex()->unlock ();
- }
-
- len = readAudio((char *)(_m_output_buf + _m_output_at), Qmmp::globalBufferSize() - _m_output_at);
-
- if(_m_length_in_bytes && len > 0)
- {
- len = qMin(_m_length_in_bytes - _m_totalBytes, len);
- len = qMax((qint64)0, len);
- }
-
- if (len > 0)
- {
- _m_bitrate = bitrate();
- _m_output_at += len;
- _m_totalBytes += len;
- if (output())
- flush();
- }
- else if (len == 0)
- {
- flush(TRUE);
- if (output())
- {
- output()->recycler()->mutex()->lock ();
- // end of stream
- while (!output()->recycler()->empty() && !_m_user_stop)
- {
- output()->recycler()->cond()->wakeOne();
- mutex()->unlock();
- output()->recycler()->cond()->wait(output()->recycler()->mutex());
- mutex()->lock ();
- }
- output()->recycler()->mutex()->unlock();
- }
-
- _m_done = TRUE;
- _m_finish = !_m_user_stop;
- }
- else
- _m_finish = TRUE;
- mutex()->unlock();
- }
-
- mutex()->lock ();
- if (_m_finish)
- finish();
- mutex()->unlock();
-}
-
-void Decoder::flush(bool final)
-{
- ulong min = final ? 0 : _m_bks;
-
- while ((!_m_done && !_m_finish) && _m_output_at > min)
- {
- output()->recycler()->mutex()->lock ();
- if(_m_seekTime >= 0)
- {
- while(output()->recycler()->used() > 1)
- output()->recycler()->done();
- output()->recycler()->mutex()->unlock ();
- _m_output_at = 0;
- break;
- }
- while ((!_m_done && !_m_finish) && output()->recycler()->full())
- {
- mutex()->unlock();
- output()->recycler()->cond()->wait(output()->recycler()->mutex());
- mutex()->lock ();
- _m_done = _m_user_stop;
- }
-
- if (_m_user_stop || _m_finish)
- _m_done = TRUE;
- else
- {
- _m_output_at -= produceSound((char*)_m_output_buf, _m_output_at, _m_bitrate, _m_chan);
- }
-
- if (output()->recycler()->full())
- {
- output()->recycler()->cond()->wakeOne();
- }
-
- output()->recycler()->mutex()->unlock();
- }
+ m_parameters = AudioParameters(srate, chan, bps);
}
-bool Decoder::nextUrlRequest(const QString &url)
+const AudioParameters Decoder::audioParameters()
{
- _m_useNextUrl = !_m_nextUrl.isEmpty() && (_m_nextUrl == url);
- return _m_useNextUrl;
+ return m_parameters;
}
// static methods
diff --git a/src/qmmp/decoder.h b/src/qmmp/decoder.h
index 78a3b6ea1..f121e5083 100644
--- a/src/qmmp/decoder.h
+++ b/src/qmmp/decoder.h
@@ -4,55 +4,29 @@
// warranty, or liability of any kind.
//
-#ifndef DECODER_H
-#define DECODER_H
+#ifndef DECODER_H
+#define DECODER_H
+
-#include <QThread>
#include <QList>
-#include <QMutex>
-#include <QWaitCondition>
-#include <QObject>
#include <QStringList>
#include <QUrl>
#include <QList>
#include <QPixmap>
#include "fileinfo.h"
+#include "audioparameters.h"
-class QObject;
-class QIODevice;
class Decoder;
class DecoderFactory;
-class Buffer;
-class Recycler;
-class Output;
-class Visualization;
-class Effect;
-class StateHandler;
/*! @brief The Decoder class provides the base interface class of audio decoders.
* @author Brad Hughes <bhughes@trolltech.com>
* @author Ilya Kotov <forkotov@hotmail.ru>
*/
-class Decoder : public QThread
+class Decoder
{
- Q_OBJECT
public:
/*!
- * Object contsructor for decoders with QIODevice-based input.
- * @param parent Parent object.
- * @param d Decoder's factory object.
- * @param input QIODevice-based input source.
- * @param output Output object.
- */
- Decoder(QObject *parent, DecoderFactory *d, QIODevice *input = 0, Output *output = 0);
- /*!
- * Object contsructor for decoders without QIODevice-based input.
- * @param parent Parent object.
- * @param d Decoder's factory object.
- * @param output Output object.
- */
- Decoder(QObject *parent, DecoderFactory *d, Output *output);
- /*!
* Destructor.
*/
virtual ~Decoder();
@@ -69,80 +43,21 @@ public:
/*!
* Requests a seek to the time \b time indicated, specified in milliseconds.
*/
- virtual void seek(qint64 time);
+ virtual void seek(qint64 time) = 0;
/*!
- * Requests playback to stop
+ * Reads up to \b maxSize bytes of decoded audio to \b data
+ * Returns the number of bytes read, or -1 if an error occurred.
+ * In most cases subclass should reimplement this function.
*/
- virtual void stop();
+ virtual qint64 read(char *data, qint64 maxSize) = 0;
/*!
* Returns current bitrate (in kbps).
* Subclass should reimplement this function.
*/
- virtual int bitrate();
- /*!
- * Requests playback to pause. If it was paused already, playback should resume.
- * Subclass with own output should reimplement this function.
- */
- virtual void pause();
- /*!
- * Tells decoder about next track. It may be useful for gapless playback.
- * @param url Url of the next item in the playlist
- */
- void setNextUrl(const QString &url);
- /*!
- * Removes information about next url
- */
- void clearNextUrl();
- /*!
- * Return \b true if the decoder can play next url without reinitialization;
- * otherwise returns \b false
- */
- bool nextUrlAccepted();
- /*!
- * Setups fragment for playback.
- * @param offset Fragment offset in milliseconds
- * @param length Fragment duration in milliseconds
- */
- void setFragment(qint64 offset, qint64 length);
- /*!
- * Returns decoder's factory object.
- */
- DecoderFactory *factory() const;
- /*!
- * Returns decoder input or 0 if input is not specified.
- */
- QIODevice *input();
- /*!
- * Returns decoder output or 0 if output is not specified.
- */
- Output *output();
- /*!
- * Returns mutex pointer.
- */
- QMutex *mutex();
- /*!
- * Returns wait condition pointer.
- */
- QWaitCondition *cond();
- /*!
- * Returns StateHandler object pointer.
- */
- StateHandler *stateHandler();
- /*!
- * Sets StateHandler pointer. May be used to override default state handler.
- * @param handler StateHandler pointer;
- */
- void setStateHandler(StateHandler *handler);
- /*!
- * Sets equalizer settings. Each item of \p bands[] and \p reamp should be \b -20.0..20.0
- * Subclass with own equalizer should reimplement this function.
- */
- virtual void setEQ(double bands[10], double preamp);
- /*!
- * Enables equalizer if \p on is \b true or disables it if \p on is \b false
- * Subclass with own equalizer should reimplement this function.
- */
- virtual void setEQEnabled(bool on);
+ virtual int bitrate() = 0;
+
+ const AudioParameters audioParameters();
+
/*!
* Returns \b true if \b file is supported by input plugins, otherwise returns \b false
*/
@@ -203,88 +118,21 @@ public:
*/
static QPixmap findCover(const QString &path);
-signals:
- /*!
- * Emitted when the decoder has finished playback.
- */
- void playbackFinished();
-
protected:
/*!
- * Reads up to \b maxSize bytes of decoded audio to \b data
- * Returns the number of bytes read, or -1 if an error occurred.
- * In most cases subclass should reimplement this function.
- */
- virtual qint64 readAudio(char *data, qint64 maxSize);
- /*!
- * Sets current playback position.
- * In most cases subclass should reimplement this function.
- * @param time New position for playback in milliseconds
- */
- virtual void seekAudio(qint64 time);
- /*!
- * The starting point for the decoding thread.
- */
- virtual void run();
- /*!
* Use this function inside initialize() reimplementation to tell other plugins about audio parameters.
* @param srate Sample rate.
* @param chan Number of channels.
* @param bps Bits per sample.
*/
void configure(quint32 srate, int chan, int bps);
- /*!
- * Sends audio data to the output plugin.
- * @param data Pointer to audio data.
- * @param size Audio data size.
- * @param brate Current bitrate (in kbps)
- * @param chan Number of channels.
- */
- qint64 produceSound(char *data, qint64 size, quint32 brate, int chan);
- /*!
- * Use this function to check next url.
- * Returns \b true if url of the next playlist item same as \b url; otherwise returns \b false
- */
- bool nextUrlRequest(const QString &url);
-
-protected slots:
- /*!
- * Subclass should call this slot when decoding is finished.
- */
- void finish();
private:
- void init();
- void flush(bool = FALSE);
static void checkFactories();
-
- DecoderFactory *_m_factory;
- QList <Effect*> _m_effects;
- QIODevice *_m_input;
- Output *_m_output;
-
- QMutex m_mutex;
- QWaitCondition m_waitCondition;
-
- uint _blksize;
- bool _m_eqInited;
- bool _m_useEQ;
- bool _m_done, _m_finish, _m_user_stop;
- bool _m_useNextUrl;
-
- QString _m_nextUrl;
-
- ulong _m_bks;
- qint64 _m_totalTime, _m_seekTime, _m_totalBytes;
- qint64 _m_offset_in_bytes, _m_length_in_bytes, _m_freq, _m_offset;
- qint64 _m_output_at;
- int _m_bitrate, _m_chan, _m_bps;
- StateHandler *_m_handler;
- unsigned char *_m_output_buf;
-
static QList<DecoderFactory*> *m_factories;
static DecoderFactory *m_lastFactory;
static QStringList m_files;
+ AudioParameters m_parameters;
};
#endif // DECODER_H
diff --git a/src/qmmp/decoderfactory.h b/src/qmmp/decoderfactory.h
index 155c1f6fb..144d3ccd7 100644
--- a/src/qmmp/decoderfactory.h
+++ b/src/qmmp/decoderfactory.h
@@ -84,13 +84,10 @@ public:
virtual const DecoderProperties properties() const = 0;
/*!
* Creates decoder object.
- * @param parent Parent object.
* @param input Input data (if required)
- * @param output Output object (if required)
* @param path File path
*/
- virtual Decoder *create(QObject *parent, QIODevice *input = 0,
- Output *output = 0, const QString &path = QString()) = 0;
+ virtual Decoder *create(QIODevice *input = 0, const QString &path = QString()) = 0;
/*!
* Extracts metadata and audio information from file \b path and returns a list of FileInfo items.
* One file may contain several playlist items (for example: cda disk or flac with embedded cue)
diff --git a/src/qmmp/output.cpp b/src/qmmp/output.cpp
index 39daf4f8b..87bce44c8 100644
--- a/src/qmmp/output.cpp
+++ b/src/qmmp/output.cpp
@@ -19,7 +19,7 @@
Output::Output (QObject* parent) : QThread (parent), m_recycler (stackSize())
{
- m_handler = 0;
+ m_handler = StateHandler::instance();
m_frequency = 0;
m_channels = 0;
m_kbps = 0;
@@ -33,6 +33,7 @@ Output::Output (QObject* parent) : QThread (parent), m_recycler (stackSize())
void Output::configure(quint32 freq, int chan, int prec)
{
+ qDebug("%u -- %d -- %d", freq, chan, prec);
m_frequency = freq;
m_channels = chan;
m_precision = prec;
diff --git a/src/qmmp/qmmp.pro b/src/qmmp/qmmp.pro
index 9783539d2..c254399c8 100644
--- a/src/qmmp/qmmp.pro
+++ b/src/qmmp/qmmp.pro
@@ -21,7 +21,10 @@ HEADERS += recycler.h \
fileinfo.h \
volumecontrol.h \
metadatamodel.h \
- tagmodel.h
+ tagmodel.h \
+ abstractengine.h \
+ qmmpaudioengine.h \
+ audioparameters.h
SOURCES += recycler.cpp \
decoder.cpp \
output.cpp \
@@ -38,8 +41,11 @@ SOURCES += recycler.cpp \
fileinfo.cpp \
volumecontrol.cpp \
metadatamodel.cpp \
- tagmodel.cpp
-
+ tagmodel.cpp \
+ abstractengine.cpp \
+ qmmpaudioengine.cpp \
+ audioparameters.cpp
+FORMS +=
unix:TARGET = ../../lib/qmmp
win32:TARGET = ../../../bin/qmmp
CONFIG += release \
@@ -60,7 +66,6 @@ contains(CONFIG, SVN_VERSION) {
unix:DEFINES += SVN_REVISION=\\\"$$system(./svn_revision.sh)\\\"
win32:DEFINES += SVN_REVISION=\\\"svn\\\"
}
-
unix {
target.path = $$LIB_DIR
devel.files += buffer.h \
diff --git a/src/qmmp/qmmpaudioengine.cpp b/src/qmmp/qmmpaudioengine.cpp
new file mode 100644
index 000000000..1e05fe348
--- /dev/null
+++ b/src/qmmp/qmmpaudioengine.cpp
@@ -0,0 +1,484 @@
+/***************************************************************************
+ * Copyright (C) 2009 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 <QMetaType>
+#include <QIODevice>
+#include <QFile>
+
+#include "effect.h"
+#include "buffer.h"
+#include "decoder.h"
+#include "output.h"
+#include "decoderfactory.h"
+#include "qmmpaudioengine.h"
+
+extern "C"
+{
+#include "equ/iir.h"
+}
+
+QmmpAudioEngine::QmmpAudioEngine(QObject *parent)
+ : AbstractEngine(parent), m_factory(0), m_input(0), m_output(0), m_eqInited(FALSE),
+ m_useEQ(FALSE)
+{
+ m_output_buf = new unsigned char[Qmmp::globalBufferSize()];
+ double b[] = {0,0,0,0,0,0,0,0,0,0};
+ setEQ(b, 0);
+ qRegisterMetaType<Qmmp::State>("Qmmp::State");
+ _blksize = Buffer::size();
+ m_effects = Effect::create(this);
+ m_bks = Buffer::size();
+ m_decoder = 0;
+ m_output = 0;
+ m_input = 0;
+ m_decoder2 = 0;
+ reset();
+}
+
+QmmpAudioEngine::~QmmpAudioEngine()
+{
+ reset();
+ if(m_output_buf)
+ delete [] m_output_buf;
+ m_output_buf = 0;
+}
+
+void QmmpAudioEngine::reset()
+{
+ m_done = FALSE;
+ m_finish = FALSE;
+ m_totalTime = 0;
+ m_seekTime = -1;
+ m_output_at = 0;
+ m_user_stop = FALSE;
+ m_bitrate = 0;
+ m_chan = 0;
+ m_bps = 0;
+ m_source.clear();
+}
+
+
+bool QmmpAudioEngine::initialize(QIODevice *input, const QString &source)
+{
+ if(m_decoder && isRunning() && m_output && m_output->isRunning())
+ {
+ m_factory = Decoder::findByPath(source);
+ m_decoder2 = m_factory->create(input, source);
+ if(!m_decoder2->initialize())
+ return FALSE;
+ if(m_decoder2->audioParameters() == m_decoder->audioParameters())
+ {
+ qDebug("accepted!!");
+ m_source = source;
+ return TRUE;
+ }
+ delete m_decoder2;
+ m_decoder2 = 0;
+ }
+ else
+ m_decoder2 = 0;
+ stop();
+ m_source = source;
+ m_output = Output::create(this);
+ m_output->recycler()->clear();
+ if (!m_output)
+ {
+ qWarning("SoundCore: unable to create output");
+ StateHandler::instance()->dispatch(Qmmp::FatalError);
+ return FALSE;
+ }
+ if (!m_output->initialize())
+ {
+ qWarning("SoundCore: unable to initialize output");
+ delete m_output;
+ m_output = 0;
+ StateHandler::instance()->dispatch(Qmmp::FatalError);
+ return FALSE;
+ }
+
+ m_factory = Decoder::findByPath(source);
+ m_decoder = m_factory->create(input, source);
+ if (!m_decoder)
+ {
+ qWarning("SoundCore: unsupported fileformat");
+ stop();
+ StateHandler::instance()->dispatch(Qmmp::NormalError);
+ return FALSE;
+ }
+ if(!m_decoder->initialize())
+ return FALSE;
+
+ m_output->configure(m_decoder->audioParameters().sampleRate(),
+ m_decoder->audioParameters().channels(),
+ m_decoder->audioParameters().bits());
+ return TRUE;
+}
+
+qint64 QmmpAudioEngine::totalTime()
+{
+ if(m_decoder)
+ return m_decoder->totalTime();
+ else
+ return 0;
+}
+
+QIODevice *QmmpAudioEngine::input()
+{
+ return m_input;
+}
+
+Output *QmmpAudioEngine::output()
+{
+ return m_output;
+}
+
+void QmmpAudioEngine::setEQ(double bands[10], double preamp)
+{
+ set_preamp(0, 1.0 + 0.0932471 *preamp + 0.00279033 * preamp * preamp);
+ set_preamp(1, 1.0 + 0.0932471 *preamp + 0.00279033 * preamp * preamp);
+ for (int i=0; i<10; ++i)
+ {
+ double value = bands[i];
+ set_gain(i,0, 0.03*value+0.000999999*value*value);
+ set_gain(i,1, 0.03*value+0.000999999*value*value);
+ }
+}
+
+void QmmpAudioEngine::setEQEnabled(bool on)
+{
+ m_useEQ = on;
+}
+
+/*void QmmpAudioEngine::configure(quint32 srate, int chan, int bps)
+{
+ Effect* effect = 0;
+ m_freq = srate;
+ m_chan = chan;
+ m_bps = bps;
+ foreach(effect, m_effects)
+ {
+ effect->configure(srate, chan, bps);
+ srate = effect->sampleRate();
+ chan = effect->channels();
+ bps = effect->bitsPerSample();
+ }
+ m_chan = chan;
+ if (m_output)
+ {
+ m_output->configure(srate, chan, bps);
+ if (!m_output_buf)
+ m_output_buf = new unsigned char[Qmmp::globalBufferSize()];
+ }
+}*/
+
+void QmmpAudioEngine::seek(qint64 time)
+{
+ if (m_output && m_output->isRunning())
+ {
+ m_output->mutex()->lock ();
+ m_output->seek(time);
+ m_output->mutex()->unlock();
+ if (isRunning())
+ {
+ mutex()->lock ();
+ m_seekTime = time;
+ mutex()->unlock();
+ }
+ }
+}
+
+int QmmpAudioEngine::bitrate()
+{
+ return 0;
+}
+
+void QmmpAudioEngine::pause()
+{
+ if (m_output)
+ {
+ m_output->mutex()->lock ();
+ m_output->pause();
+ m_output->mutex()->unlock();
+ }
+
+ // wake up threads
+ if (m_decoder)
+ {
+ mutex()->lock ();
+ cond()->wakeAll();
+ mutex()->unlock();
+ }
+
+ if (m_output)
+ {
+ m_output->recycler()->mutex()->lock ();
+ m_output->recycler()->cond()->wakeAll();
+ m_output->recycler()->mutex()->unlock();
+ }
+
+}
+
+void QmmpAudioEngine::stop()
+{
+ mutex()->lock ();
+ m_user_stop = TRUE;
+ mutex()->unlock();
+
+ if (m_output)
+ {
+ m_output->mutex()->lock ();
+ m_output->stop();
+ m_output->mutex()->unlock();
+ }
+
+ // wake up threads
+
+ mutex()->lock ();
+ cond()->wakeAll();
+ mutex()->unlock();
+
+ if (m_output)
+ {
+ m_output->recycler()->mutex()->lock ();
+ m_output->recycler()->cond()->wakeAll();
+ m_output->recycler()->mutex()->unlock();
+ }
+
+ wait();
+ if (m_output)
+ m_output->wait();
+
+ if (m_output)
+ {
+ delete m_output;
+ m_output = 0;
+ }
+
+ if(m_decoder)
+ {
+ delete m_decoder;
+ m_decoder = 0;
+ }
+ reset();
+}
+
+qint64 QmmpAudioEngine::produceSound(char *data, qint64 size, quint32 brate, int chan)
+{
+ ulong sz = size < _blksize ? size : _blksize;
+
+ if (m_useEQ)
+ {
+ if (!m_eqInited)
+ {
+ init_iir();
+ m_eqInited = TRUE;
+ }
+ iir((void*) data, sz, chan);
+ }
+ char *out_data = data;
+ char *prev_data = data;
+ qint64 w = sz;
+ Effect* effect = 0;
+ foreach(effect, m_effects)
+ {
+ w = effect->process(prev_data, sz, &out_data);
+
+ if (w <= 0)
+ {
+ // copy data if plugin can not process it
+ w = sz;
+ out_data = new char[w];
+ memcpy(out_data, prev_data, w);
+ }
+ if (data != prev_data)
+ delete prev_data;
+ prev_data = out_data;
+ }
+
+ Buffer *b = output()->recycler()->get(w);
+
+ memcpy(b->data, out_data, w);
+
+ if (data != out_data)
+ delete out_data;
+
+ if (w < _blksize + b->exceeding)
+ memset(b->data + w, 0, _blksize + b->exceeding - w);
+
+ b->nbytes = w;
+ b->rate = brate;
+
+ output()->recycler()->add();
+
+ size -= sz;
+ memmove(data, data + sz, size);
+ return sz;
+}
+
+void QmmpAudioEngine::finish()
+{
+ if (output())
+ {
+ output()->mutex()->lock ();
+ output()->finish();
+ output()->mutex()->unlock();
+ }
+ emit playbackFinished();
+}
+
+void QmmpAudioEngine::run()
+{
+ Q_ASSERT(m_chan == 0);
+ Q_ASSERT(!m_output_buf);
+ mutex()->lock ();
+ if (m_output)
+ m_output->start();
+ qint64 len = 0;
+ mutex()->unlock();
+
+ if (QFile::exists(m_source)) //send metadata for local files
+ {
+ QList <FileInfo *> list = m_factory->createPlayList(m_source, TRUE);
+ if (!list.isEmpty())
+ {
+ StateHandler::instance()->dispatch(list[0]->metaData());
+ while (!list.isEmpty())
+ delete list.takeFirst();
+ }
+ }
+
+ while (! m_done && ! m_finish)
+ {
+ mutex()->lock ();
+ // decode
+
+ if (m_seekTime >= 0)
+ {
+ m_decoder->seek(m_seekTime);
+ m_seekTime = -1;
+ output()->recycler()->mutex()->lock ();
+ while(output()->recycler()->used() > 1)
+ output()->recycler()->done();
+ output()->recycler()->mutex()->unlock ();
+ }
+
+ len = m_decoder->read((char *)(m_output_buf + m_output_at),
+ Qmmp::globalBufferSize() - m_output_at);
+
+ if (len > 0)
+ {
+ m_bitrate = m_decoder->bitrate();
+ m_output_at += len;
+ if (output())
+ flush();
+ }
+ else if (len == 0)
+ {
+ qDebug("0");
+ if(m_decoder2)
+ {
+ qDebug("next decoder");
+ delete m_decoder;
+ m_decoder = m_decoder2;
+ emit playbackFinished();
+ StateHandler::instance()->dispatch(Qmmp::Stopped);
+ StateHandler::instance()->dispatch(Qmmp::Buffering);
+ StateHandler::instance()->dispatch(Qmmp::Playing);
+ m_output->seek(0);
+ mutex()->unlock();
+ if (QFile::exists(m_source)) //send metadata for local files
+ {
+ QList <FileInfo *> list = m_factory->createPlayList(m_source, TRUE);
+ if (!list.isEmpty())
+ {
+ StateHandler::instance()->dispatch(list[0]->metaData());
+ while (!list.isEmpty())
+ delete list.takeFirst();
+ }
+ }
+ continue;
+ }
+
+ flush(TRUE);
+ if (output())
+ {
+ output()->recycler()->mutex()->lock ();
+ // end of stream
+ while (!output()->recycler()->empty() && !m_user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+ m_done = TRUE;
+ m_finish = !m_user_stop;
+ }
+ else
+ m_finish = TRUE;
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+ if (m_finish)
+ finish();
+ mutex()->unlock();
+}
+
+void QmmpAudioEngine::flush(bool final)
+{
+ ulong min = final ? 0 : m_bks;
+
+ while ((!m_done && !m_finish) && m_output_at > min)
+ {
+ output()->recycler()->mutex()->lock ();
+ if(m_seekTime >= 0)
+ {
+ while(output()->recycler()->used() > 1)
+ output()->recycler()->done();
+ output()->recycler()->mutex()->unlock ();
+ m_output_at = 0;
+ break;
+ }
+ while ((!m_done && !m_finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ m_done = m_user_stop;
+ }
+
+ if (m_user_stop || m_finish)
+ m_done = TRUE;
+ else
+ {
+ m_output_at -= produceSound((char*)m_output_buf, m_output_at, m_bitrate, m_chan);
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
diff --git a/src/qmmp/qmmpaudioengine.h b/src/qmmp/qmmpaudioengine.h
new file mode 100644
index 000000000..74f4c70eb
--- /dev/null
+++ b/src/qmmp/qmmpaudioengine.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2009 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 QMMPAUDIOENGINE_H
+#define QMMPAUDIOENGINE_H
+
+#include "abstractengine.h"
+
+class QIODevice;
+class Output;
+class Effect;
+class DecoderFactory;
+class StateHandler;
+class Decoder;
+
+
+class QmmpAudioEngine : public AbstractEngine
+{
+Q_OBJECT
+public:
+ QmmpAudioEngine(QObject *parent);
+ ~QmmpAudioEngine();
+
+ bool initialize(QIODevice *input, const QString &source);
+ qint64 totalTime();
+ void seek(qint64 time);
+ void stop();
+ int bitrate();
+ void pause();
+ QIODevice *input();
+ Output *output();
+ void setEQ(double bands[10], double preamp);
+ void setEQEnabled(bool on);
+
+signals:
+ void playbackFinished();
+
+protected:
+ void run();
+
+
+protected slots:
+ void finish();
+
+private:
+ void reset();
+ void flush(bool = FALSE);
+ qint64 produceSound(char *data, qint64 size, quint32 brate, int chan);
+
+ DecoderFactory *m_factory;
+ QList <Effect*> m_effects;
+ QIODevice *m_input;
+ Output *m_output;
+
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+
+ uint _blksize;
+ bool m_eqInited;
+ bool m_useEQ;
+ bool m_done, m_finish, m_user_stop;
+ ulong m_bks;
+ qint64 m_totalTime, m_seekTime;
+ qint64 m_output_at;
+ int m_bitrate, m_chan, m_bps;
+ unsigned char *m_output_buf;
+ Decoder *m_decoder;
+ QString m_source;
+ Decoder *m_decoder2;
+};
+
+#endif // QMMPAUDIOENGINE_H
diff --git a/src/qmmp/soundcore.cpp b/src/qmmp/soundcore.cpp
index 44dbac11f..cc48a33f1 100644
--- a/src/qmmp/soundcore.cpp
+++ b/src/qmmp/soundcore.cpp
@@ -24,6 +24,7 @@
#include <QSettings>
#include <QDir>
+#include "qmmpaudioengine.h"
#include "decoderfactory.h"
#include "streamreader.h"
#include "effect.h"
@@ -50,6 +51,7 @@ SoundCore::SoundCore(QObject *parent)
m_vis = 0;
m_parentWidget = 0;
m_factory = 0;
+ m_engine = 0;
for (int i = 1; i < 10; ++i)
m_bands[i] = 0;
m_handler = new StateHandler(this);
@@ -71,26 +73,15 @@ SoundCore::~SoundCore()
stop();
}
-bool SoundCore::play(const QString &source)
+bool SoundCore::play(const QString &source, bool queue)
{
- if(m_decoder && m_decoder->nextUrlAccepted()) //decoder can play next url
- {
- //fake stop/start cycle
- m_handler->dispatch(Qmmp::Stopped);
- m_handler->dispatch(Qmmp::Playing);
- m_handler->dispatch(Qmmp::Buffering);
- m_handler->dispatch(Qmmp::Playing);
- m_source = source;
- m_decoder->clearNextUrl();
- return TRUE;
- }
- stop();
-
+ if(!queue)
+ stop();
m_source = source;
- if (m_handler->state() != Qmmp::Stopped) //clear error state
+ /*if (m_handler->state() != Qmmp::Stopped) //clear error state
m_handler->dispatch(Qmmp::Stopped);
- m_handler->dispatch(Qmmp::Buffering); //buffering state
+ m_handler->dispatch(Qmmp::Buffering); //buffering state*/
QUrl url;
if (source.contains("://")) //url
@@ -144,64 +135,26 @@ bool SoundCore::play(const QString &source)
return FALSE;
}
+//bool enqueue(const QString &url);
+
void SoundCore::setNextUrl(const QString &source)
{
- if(m_decoder)
- m_decoder->setNextUrl(source);
+ /*if(m_decoder)
+ m_decoder->setNextUrl(source);*/
}
void SoundCore::clearNextUrl()
{
- if(m_decoder)
- m_decoder->clearNextUrl();
+ /*if(m_decoder)
+ m_decoder->clearNextUrl();*/
}
void SoundCore::stop()
{
m_factory = 0;
m_source.clear();
- if (m_decoder /*&& m_decoder->isRunning()*/)
- {
- m_decoder->mutex()->lock ();
- m_decoder->stop();
- m_decoder->mutex()->unlock();
- //m_decoder->stateHandler()->dispatch(Qmmp::Stopped);
- }
- if (m_output)
- {
- m_output->mutex()->lock ();
- m_output->stop();
- m_output->mutex()->unlock();
- }
-
- // wake up threads
- if (m_decoder)
- {
- m_decoder->mutex()->lock ();
- m_decoder->cond()->wakeAll();
- m_decoder->mutex()->unlock();
- }
- if (m_output)
- {
- m_output->recycler()->mutex()->lock ();
- m_output->recycler()->cond()->wakeAll();
- m_output->recycler()->mutex()->unlock();
- }
- if (m_decoder)
- m_decoder->wait();
- if (m_output)
- m_output->wait();
-
- if (m_output)
- {
- delete m_output;
- m_output = 0;
- }
- if (m_decoder)
- {
- delete m_decoder;
- m_decoder = 0;
- }
+ if(m_engine)
+ m_engine->stop();
if (m_input)
{
m_input->deleteLater();
@@ -215,55 +168,14 @@ void SoundCore::stop()
void SoundCore::pause()
{
- if (m_output)
- {
- m_output->mutex()->lock ();
- m_output->pause();
- m_output->mutex()->unlock();
- }
- else if (m_decoder)
- {
- m_decoder->mutex()->lock ();
- m_decoder->pause();
- m_decoder->mutex()->unlock();
- }
-
- // wake up threads
- if (m_decoder)
- {
- m_decoder->mutex()->lock ();
- m_decoder->cond()->wakeAll();
- m_decoder->mutex()->unlock();
- }
-
- if (m_output)
- {
- m_output->recycler()->mutex()->lock ();
- m_output->recycler()->cond()->wakeAll();
- m_output->recycler()->mutex()->unlock();
- }
+ if(m_engine)
+ m_engine->pause();
}
void SoundCore::seek(qint64 pos)
{
- if (m_output && m_output->isRunning())
- {
- m_output->mutex()->lock ();
- m_output->seek(pos);
- m_output->mutex()->unlock();
- if (m_decoder && m_decoder->isRunning())
- {
- m_decoder->mutex()->lock ();
- m_decoder->seek(pos);
- m_decoder->mutex()->unlock();
- }
- }
- else if (m_decoder)
- {
- m_decoder->mutex()->lock ();
- m_decoder->seek(pos);
- m_decoder->mutex()->unlock();
- }
+ if(m_engine)
+ m_engine->seek(pos);
}
const QString SoundCore::url()
@@ -273,7 +185,7 @@ const QString SoundCore::url()
qint64 SoundCore::totalTime() const
{
- return (m_decoder) ? m_decoder->totalTime() : 0;
+ return (m_engine) ? m_engine->totalTime() : 0;
}
void SoundCore::setEQ(double bands[10], double preamp)
@@ -283,23 +195,23 @@ void SoundCore::setEQ(double bands[10], double preamp)
m_preamp = preamp;
if (m_decoder)
{
- m_decoder->mutex()->lock ();
- m_decoder->setEQ(m_bands, m_preamp);
- m_decoder->setEQEnabled(m_useEQ);
- m_decoder->mutex()->unlock();
+ m_engine->mutex()->lock ();
+ m_engine->setEQ(m_bands, m_preamp);
+ m_engine->setEQEnabled(m_useEQ);
+ m_engine->mutex()->unlock();
}
}
void SoundCore::setEQEnabled(bool on)
{
- m_useEQ = on;
+ /*m_useEQ = on;
if (m_decoder)
{
m_decoder->mutex()->lock ();
m_decoder->setEQ(m_bands, m_preamp);
m_decoder->setEQEnabled(on);
m_decoder->mutex()->unlock();
- }
+ }*/
}
void SoundCore::setVolume(int L, int R)
@@ -319,14 +231,14 @@ int SoundCore::rightVolume()
void SoundCore::setSoftwareVolume(bool b)
{
- SoftwareVolume::setEnabled(b);
+ /*SoftwareVolume::setEnabled(b);
if (m_decoder)
m_decoder->mutex()->lock();
delete m_volumeControl;
m_volumeControl = VolumeControl::create(this);
connect(m_volumeControl, SIGNAL(volumeChanged(int, int)), SIGNAL(volumeChanged(int, int)));
if (m_decoder)
- m_decoder->mutex()->unlock();
+ m_decoder->mutex()->unlock();*/
}
bool SoundCore::softwareVolume()
@@ -391,61 +303,23 @@ bool SoundCore::decode()
return FALSE;
}
}
- if (!m_factory->properties().noOutput)
+
+ if(!m_engine)
{
- m_output = Output::create(this);
- if (!m_output)
- {
- qWarning("SoundCore: unable to create output");
- m_handler->dispatch(Qmmp::FatalError);
- return FALSE;
- }
- if (!m_output->initialize())
- {
- qWarning("SoundCore: unable to initialize output");
- delete m_output;
- m_output = 0;
- m_handler->dispatch(Qmmp::FatalError);
- return FALSE;
- }
+ m_engine = new QmmpAudioEngine(this);
+ connect(m_engine, SIGNAL(playbackFinished()), SIGNAL(finished()));
}
- m_decoder = m_factory->create(this, m_input, m_output, m_source);
- if (!m_decoder)
- {
- qWarning("SoundCore: unsupported fileformat");
- m_block = FALSE;
- stop();
- m_handler->dispatch(Qmmp::NormalError);
+
+ if(m_engine->initialize(m_input, m_source))
+ m_engine->start();
+ else
return FALSE;
- }
- m_decoder->setStateHandler(m_handler);
+
setEQ(m_bands, m_preamp);
setEQEnabled(m_useEQ);
qDebug ("ok");
- connect(m_decoder, SIGNAL(playbackFinished()), SIGNAL(finished()));
- if (m_output)
- m_output->setStateHandler(m_decoder->stateHandler());
- if (m_decoder->initialize())
- {
- if (QFile::exists(m_source)) //send metadata for local files
- {
- QList <FileInfo *> list = m_factory->createPlayList(m_source, TRUE);
- if (!list.isEmpty())
- {
- m_handler->dispatch(list[0]->metaData());
- while (!list.isEmpty())
- delete list.takeFirst();
- }
- }
-
- if (m_output)
- m_output->start();
- m_decoder->start();
- return TRUE;
- }
- stop();
- return FALSE;
+ return TRUE;
}
SoundCore* SoundCore::instance()
diff --git a/src/qmmp/soundcore.h b/src/qmmp/soundcore.h
index f099318e3..c15bce69b 100644
--- a/src/qmmp/soundcore.h
+++ b/src/qmmp/soundcore.h
@@ -29,6 +29,7 @@
class QIODevice;
class VolumeControl;
+class QmmpAudioEngine;
/*! \brief The SoundCore class provides a simple interface for audio playback.
* @author Ilya Kotov <forkotov02@hotmail.ru>
@@ -103,6 +104,9 @@ public:
* Returns the metdata string associated with the given \b key.
*/
QString metaData(Qmmp::MetaData key);
+
+ //bool enqueue(const QString &url);
+
/*!
* Returns a pointer to the SoundCore instance.
*/
@@ -126,7 +130,7 @@ public slots:
* Returns \b true if playback started successful or source is not a local file,
* otherwise returns \b false. Useful for invalid files skipping.
*/
- bool play(const QString &source);
+ bool play(const QString &source, bool queue = FALSE);
/*!
* Tells decoder about next track. It may be useful for gapless playback.
* @param source Url of the next item in the playlist
@@ -229,6 +233,7 @@ private:
static SoundCore* m_instance;
StateHandler *m_handler;
VolumeControl *m_volumeControl;
+ QmmpAudioEngine *m_engine;
};
#endif
diff --git a/src/qmmpui/mediaplayer.cpp b/src/qmmpui/mediaplayer.cpp
index 42a0cf6b8..d0f55ba9e 100644
--- a/src/qmmpui/mediaplayer.cpp
+++ b/src/qmmpui/mediaplayer.cpp
@@ -63,6 +63,7 @@ void MediaPlayer::initialize(SoundCore *core, PlayListModel *model)
m_core = core;
m_model = model;
m_repeat = FALSE;
+ connect(m_core, SIGNAL(aboutToFinish()), SLOT(updateNextUrl()));
connect(m_core, SIGNAL(finished()), SLOT(next()));
//connect(m_model, SIGNAL(listChanged()), SLOT(updateNextUrl()));
}
@@ -79,7 +80,7 @@ bool MediaPlayer::isRepeatable() const
void MediaPlayer::play()
{
- disconnect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
+ //disconnect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
m_model->doCurrentVisibleRequest();
if (m_core->state() == Qmmp::Paused)
{
@@ -92,7 +93,15 @@ void MediaPlayer::play()
QString s = m_model->currentItem()->url();
if (s.isEmpty())
+ {
+ m_nextUrl.clear();
+ return;
+ }
+ if(m_nextUrl == s)
+ {
+ m_nextUrl.clear();
return;
+ }
if (!m_core->play(s))
{
@@ -132,19 +141,20 @@ void MediaPlayer::play()
else
{
m_skips = 0;
- updateNextUrl();
- connect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
+ /*updateNextUrl();
+ connect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));*/
}
}
void MediaPlayer::stop()
{
m_core->stop();
+ m_nextUrl.clear();
}
void MediaPlayer::next()
{
- disconnect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
+ //disconnect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
if (!m_model->isEmptyQueue())
{
m_model->setCurrentToQueued();
@@ -165,7 +175,7 @@ void MediaPlayer::next()
void MediaPlayer::previous()
{
- disconnect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
+ //disconnect(m_model, SIGNAL(listChanged()), this, SLOT(updateNextUrl()));
if (!m_model->previous())
{
stop();
@@ -194,13 +204,16 @@ void MediaPlayer::setRepeatable(bool r)
}
m_repeat = r;
emit repeatableChanged(r);
- updateNextUrl();
+ //updateNextUrl();
}
void MediaPlayer::updateNextUrl()
{
if(m_model->nextItem() && !isRepeatable())
- m_core->setNextUrl(m_model->nextItem()->url());
+ {
+ m_core->play(m_model->nextItem()->url(), TRUE);
+ m_nextUrl = m_model->nextItem()->url();
+ }
else
- m_core->clearNextUrl();
+ m_nextUrl.clear();
}
diff --git a/src/qmmpui/mediaplayer.h b/src/qmmpui/mediaplayer.h
index 327b9c980..d0b84fad3 100644
--- a/src/qmmpui/mediaplayer.h
+++ b/src/qmmpui/mediaplayer.h
@@ -99,6 +99,7 @@ private:
static MediaPlayer* m_instance;
bool m_repeat;
int m_skips;
+ QString m_nextUrl;
};
#endif