/*************************************************************************** * Copyright (C) 2008-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 #include #include #include #include #include #include #include #include "cueparser.h" #include "decoder_cue.h" DecoderCUE::DecoderCUE(QObject *parent, DecoderFactory *d, const QString &url) : Decoder(parent, d) { path = url; m_decoder = 0; m_output2 = 0; m_input2 = 0; for (int i = 1; i < 10; ++i) m_bands2[i] = 0; m_preamp2 = 0; m_useEQ2 = FALSE; } DecoderCUE::~DecoderCUE() {} bool DecoderCUE::initialize() { m_input2 = 0; QString p = QUrl(path).path(); p.replace(QString(QUrl::toPercentEncoding("#")), "#"); p.replace(QString(QUrl::toPercentEncoding("%")), "%"); CUEParser parser(p); if (parser.count() == 0) { qWarning("DecoderCUE: invalid cue file"); return FALSE; } int track = path.section("#", -1).toInt(); path = parser.filePath(track); // find next track if(track <= parser.count() - 1) m_nextUrl = parser.info(track + 1)->path(); //is it track of another file? if(QUrl(m_nextUrl).path() != p) m_nextUrl.clear(); if (!QFile::exists(path)) { qWarning("DecoderCUE: file \"%s\" doesn't exist", qPrintable(path)); return FALSE; } DecoderFactory *df = Decoder::findByPath(path); if (!df) { qWarning("DecoderCUE: unsupported file format"); return FALSE; } if (!df->properties().noInput) { m_input2 = new QFile(path); if (!m_input2->open(QIODevice::ReadOnly)) { qDebug("DecoderCUE: cannot open input"); stop(); return FALSE; } } if (df->properties().noOutput) { qWarning("DecoderCUE: unsupported file format"); return FALSE; } m_output2 = Output::create(this); if (!m_output2) { qWarning("DecoderCUE: unable to create output"); return FALSE; } if (!m_output2->initialize()) { qWarning("SoundCore: unable to initialize output"); delete m_output2; m_output2 = 0; return FALSE; } m_length = parser.length(track); m_offset = parser.offset(track); m_decoder = df->create(this, m_input2, m_output2, path); m_decoder->setEQ(m_bands2, m_preamp2); m_decoder->setEQEnabled(m_useEQ2); connect(m_decoder, SIGNAL(playbackFinished()), SLOT(finish())); //replace default state handler to ignore metadata m_decoder->setStateHandler(new CUEStateHandler(m_decoder)); m_output2->setStateHandler(m_decoder->stateHandler()); connect(stateHandler(), SIGNAL(aboutToFinish()), SLOT(proccessFinish())); //prepare decoder and ouput objects m_decoder->initialize(); m_decoder->setFragment(m_offset, m_length); //send metadata QMap metaData = parser.info(track)->metaData(); stateHandler()->dispatch(metaData); return TRUE; } qint64 DecoderCUE::totalTime() { return m_decoder ? m_length : 0; } void DecoderCUE::seek(qint64 pos) { if (m_output2 && m_output2->isRunning()) { m_output2->mutex()->lock (); m_output2->seek(pos); m_output2->mutex()->unlock(); if (m_decoder && m_decoder->isRunning()) { m_decoder->mutex()->lock (); m_decoder->seek(pos); m_decoder->mutex()->unlock(); } } } void DecoderCUE::stop() { 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_output2) { m_output2->mutex()->lock (); m_output2->stop(); m_output2->mutex()->unlock(); } // wake up threads if (m_decoder) { m_decoder->mutex()->lock (); m_decoder->cond()->wakeAll(); m_decoder->mutex()->unlock(); } if (m_output2) { m_output2->recycler()->mutex()->lock (); m_output2->recycler()->cond()->wakeAll(); m_output2->recycler()->mutex()->unlock(); } if (m_decoder) m_decoder->wait(); if (m_output2) m_output2->wait(); if (m_input2) { m_input2->deleteLater(); m_input2 = 0; } } void DecoderCUE::pause() { if (m_output2) { m_output2->mutex()->lock (); m_output2->pause(); m_output2->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_output2) { m_output2->recycler()->mutex()->lock (); m_output2->recycler()->cond()->wakeAll(); m_output2->recycler()->mutex()->unlock(); } } void DecoderCUE::setEQ(double bands[10], double preamp) { for (int i = 0; i < 10; ++i) m_bands2[i] = bands[i]; m_preamp2 = preamp; if (m_decoder) { m_decoder->mutex()->lock (); m_decoder->setEQ(m_bands2, m_preamp2); m_decoder->setEQEnabled(m_useEQ2); m_decoder->mutex()->unlock(); } } void DecoderCUE::setEQEnabled(bool on) { m_useEQ2 = on; if (m_decoder) { m_decoder->mutex()->lock (); m_decoder->setEQ(m_bands2, m_preamp2); m_decoder->setEQEnabled(on); m_decoder->mutex()->unlock(); } } void DecoderCUE::run() { m_decoder->start(); if (m_output2) m_output2->start(); } void DecoderCUE::proccessFinish() { qDebug("==>%s", qPrintable(SoundCore::instance()->nextUrl())); qDebug("==>%s", qPrintable(m_nextUrl)); if(!SoundCore::instance()->nextUrl().isEmpty() && !m_nextUrl.isEmpty() && SoundCore::instance()->nextUrl() == m_nextUrl) { qDebug("prefinish"); qDebug("next url: %s", qPrintable(m_nextUrl)); int track = m_nextUrl.section("#", -1).toInt(); QString p = QUrl(m_nextUrl).path(); p.replace(QString(QUrl::toPercentEncoding("#")), "#"); p.replace(QString(QUrl::toPercentEncoding("%")), "%"); CUEParser parser(p); m_length = parser.length(track); m_offset = parser.offset(track); m_decoder->mutex()->lock(); m_decoder->setFragment(m_offset, m_length); m_output2->seek(0); m_decoder->mutex()->unlock(); //stateHandler()->dispatch(Qmmp::Stopped); //stateHandler()->dispatch(Qmmp::Buffering); //stateHandler()->dispatch(Qmmp::Playing); //m_decoder->mutex()->lock(); //m_output2->seek(0); //m_decoder->mutex()->unlock(); // find next track if(track <= parser.count() - 1) m_nextUrl = parser.info(track + 1)->path(); else m_nextUrl.clear(); //is it track of another file? if(QUrl(m_nextUrl).path() != p) m_nextUrl.clear(); emit playbackFinished(); } else Decoder::stop(); } CUEStateHandler::CUEStateHandler(QObject *parent): StateHandler(parent){} CUEStateHandler::~CUEStateHandler(){} void CUEStateHandler::dispatch(qint64 elapsed, int bitrate, quint32 frequency, int precision, int channels) { StateHandler::instance()->dispatch(elapsed, bitrate, frequency, precision, channels); } void CUEStateHandler::dispatch(const QMap &metaData) { //ignore media file metadata Q_UNUSED(metaData) } void CUEStateHandler::dispatch(const Qmmp::State &state) { StateHandler::instance()->dispatch(state); }