diff options
| author | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2009-09-12 14:33:49 +0000 |
|---|---|---|
| committer | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2009-09-12 14:33:49 +0000 |
| commit | 8fbf582b60b6267463e6a3dc289b52e49fdc2f83 (patch) | |
| tree | 9540a7b90f2b52ccb34104f2f89d4c1e22445a70 /src | |
| parent | c89ba4ee3ccca9a2f4b38d4b76ca666f94babbd4 (diff) | |
| download | qmmp-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.cpp | 39 | ||||
| -rw-r--r-- | src/plugins/Input/mad/decoder_mad.h | 9 | ||||
| -rw-r--r-- | src/plugins/Input/mad/decodermadfactory.cpp | 4 | ||||
| -rw-r--r-- | src/plugins/Input/mad/decodermadfactory.h | 2 | ||||
| -rw-r--r-- | src/qmmp/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/qmmp/abstractengine.cpp | 35 | ||||
| -rw-r--r-- | src/qmmp/abstractengine.h | 124 | ||||
| -rw-r--r-- | src/qmmp/audioparameters.cpp | 54 | ||||
| -rw-r--r-- | src/qmmp/audioparameters.h | 27 | ||||
| -rw-r--r-- | src/qmmp/decoder.cpp | 374 | ||||
| -rw-r--r-- | src/qmmp/decoder.h | 182 | ||||
| -rw-r--r-- | src/qmmp/decoderfactory.h | 5 | ||||
| -rw-r--r-- | src/qmmp/output.cpp | 3 | ||||
| -rw-r--r-- | src/qmmp/qmmp.pro | 13 | ||||
| -rw-r--r-- | src/qmmp/qmmpaudioengine.cpp | 484 | ||||
| -rw-r--r-- | src/qmmp/qmmpaudioengine.h | 89 | ||||
| -rw-r--r-- | src/qmmp/soundcore.cpp | 202 | ||||
| -rw-r--r-- | src/qmmp/soundcore.h | 7 | ||||
| -rw-r--r-- | src/qmmpui/mediaplayer.cpp | 29 | ||||
| -rw-r--r-- | src/qmmpui/mediaplayer.h | 1 |
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 |
