diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/3rdparty/phonon/waveout | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/3rdparty/phonon/waveout')
-rw-r--r-- | src/3rdparty/phonon/waveout/audiooutput.cpp | 78 | ||||
-rw-r--r-- | src/3rdparty/phonon/waveout/audiooutput.h | 65 | ||||
-rw-r--r-- | src/3rdparty/phonon/waveout/backend.cpp | 131 | ||||
-rw-r--r-- | src/3rdparty/phonon/waveout/backend.h | 69 | ||||
-rw-r--r-- | src/3rdparty/phonon/waveout/mediaobject.cpp | 686 | ||||
-rw-r--r-- | src/3rdparty/phonon/waveout/mediaobject.h | 162 |
6 files changed, 1191 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/waveout/audiooutput.cpp b/src/3rdparty/phonon/waveout/audiooutput.cpp new file mode 100644 index 0000000..f842dc9 --- /dev/null +++ b/src/3rdparty/phonon/waveout/audiooutput.cpp @@ -0,0 +1,78 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiooutput.h" +#include "mediaobject.h" + +#include <QtCore/QVector> + +#include <cmath> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + AudioOutput::AudioOutput(Backend *, QObject *parent) + { + setParent(parent); + m_volume = 0xffff; + } + + AudioOutput::~AudioOutput() + { + } + + int AudioOutput::outputDevice() const + { + return 0; + } + + void AudioOutput::setVolume(qreal newVolume) + { + m_volume = newVolume; + emit volumeChanged(newVolume); + } + + void AudioOutput::setCrossFadingProgress(short currentIndex, qreal progress) + { + Q_UNUSED(currentIndex); + Q_UNUSED(progress); + } + + bool AudioOutput::setOutputDevice(const AudioOutputDevice & newDevice) + { + return setOutputDevice(newDevice.index()); + } + + qreal AudioOutput::volume() const + { + return m_volume; + } + + bool AudioOutput::setOutputDevice(int newDevice) + { + + return (newDevice == 0); + } + + } +} + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/waveout/audiooutput.h b/src/3rdparty/phonon/waveout/audiooutput.h new file mode 100644 index 0000000..43f8222 --- /dev/null +++ b/src/3rdparty/phonon/waveout/audiooutput.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_AUDIOOUTPUT_H +#define PHONON_AUDIOOUTPUT_H + +#include <QtCore/QFile> +#include <phonon/audiooutputinterface.h> + +#include "backend.h" + +struct IBaseFilter; +struct IBasicAudio; + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + class AudioOutput : public QObject, public Phonon::AudioOutputInterface + { + Q_OBJECT + + Q_INTERFACES(Phonon::AudioOutputInterface) + public: + AudioOutput(Backend *back, QObject *parent); + ~AudioOutput(); + + // Attributes Getters: + qreal volume() const; + int outputDevice() const; + void setVolume(qreal newVolume); + bool setOutputDevice(int newDevice); + bool setOutputDevice(const AudioOutputDevice & newDevice); + void setCrossFadingProgress(short currentIndex, qreal progress); + + Q_SIGNALS: + void audioDeviceFailed(); + void volumeChanged(qreal); + private: + unsigned int m_volume; + + + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/waveout/backend.cpp b/src/3rdparty/phonon/waveout/backend.cpp new file mode 100644 index 0000000..8faa26e --- /dev/null +++ b/src/3rdparty/phonon/waveout/backend.cpp @@ -0,0 +1,131 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "backend.h" + + +#include "audiooutput.h" +#include "mediaobject.h" + + + +#include <QtCore/QSettings> +#include <QtCore/QSet> +#include <QtCore/QVariant> +#include <QtCore/QStringList> + +#include <QtCore/QtPlugin> + + +QT_BEGIN_NAMESPACE + +// export as Qt/KDE factory as required + +Q_EXPORT_PLUGIN2(phonon_waveout, Phonon::WaveOut::Backend); + +namespace Phonon +{ + namespace WaveOut + { + + Backend::Backend(QObject *parent, const QVariantList &) + : QObject(parent) + { + } + + Backend::~Backend() + { + } + + QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) + { + Q_UNUSED(args); + switch (c) + { + case MediaObjectClass: + return new MediaObject(parent); + case AudioOutputClass: + return new AudioOutput(this, parent); + default: + return 0; + } + } + + bool Backend::supportsVideo() const + { + return false; + } + + QStringList Backend::availableMimeTypes() const + { + QStringList ret; + return ret; + } + + + QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const + { + QList<int> r; + if (type == Phonon::AudioOutputDeviceType) + r.append(0); + return r; + } + + QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const + { + Q_UNUSED(index); + QHash<QByteArray, QVariant> r; + if (type == Phonon::AudioOutputDeviceType) + r["name"] = QLatin1String("default audio device"); + return r; + } + + + bool Backend::connectNodes(QObject *node1, QObject *node2) + { + MediaObject *mediaObject = qobject_cast<MediaObject*> (node1); + AudioOutput *audioOutput = qobject_cast<AudioOutput*> (node2); + + if (mediaObject && audioOutput) + mediaObject->setAudioOutput(audioOutput); + return true; + } + + bool Backend::disconnectNodes(QObject *node1, QObject *node2) + { + Q_UNUSED(node1); + Q_UNUSED(node2); + return true; + } + + //transaction management + bool Backend::startConnectionChange(QSet<QObject *>) + { + return true; + } + + bool Backend::endConnectionChange(QSet<QObject *>) + { + return true; + } + + } +} + +QT_END_NAMESPACE + +#include "moc_backend.cpp" diff --git a/src/3rdparty/phonon/waveout/backend.h b/src/3rdparty/phonon/waveout/backend.h new file mode 100644 index 0000000..060d853 --- /dev/null +++ b/src/3rdparty/phonon/waveout/backend.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_BACKEND_H +#define PHONON_BACKEND_H + +#include <phonon/backendinterface.h> +#include <phonon/phononnamespace.h> + +#include <QtCore/QList> + + + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + class AudioOutput; + class MediaObject; + + class Backend : public QObject, public Phonon::BackendInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::BackendInterface) + public: + Backend(QObject *parent = 0, const QVariantList & = QVariantList()); + virtual ~Backend(); + + QObject *createObject(Phonon::BackendInterface::Class, QObject *parent, const QList<QVariant> &args); + + bool supportsVideo() const; + QStringList availableMimeTypes() const; + + QList<int> objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const; + QHash<QByteArray, QVariant> objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const; + + bool connectNodes(QObject *, QObject *); + bool disconnectNodes(QObject *, QObject *); + + //transaction management + bool startConnectionChange(QSet<QObject *>); + bool endConnectionChange(QSet<QObject *>); + + Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_BACKEND_H diff --git a/src/3rdparty/phonon/waveout/mediaobject.cpp b/src/3rdparty/phonon/waveout/mediaobject.cpp new file mode 100644 index 0000000..35d9e0b --- /dev/null +++ b/src/3rdparty/phonon/waveout/mediaobject.cpp @@ -0,0 +1,686 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "mediaobject.h" +#include "audiooutput.h" + +#include <QtCore/QVector> +#include <QtCore/QTimerEvent> +#include <QtCore/QTimer> +#include <QtCore/QTime> +#include <QtCore/QLibrary> +#include <QtCore/QUrl> +#include <QtCore/QWriteLocker> + +#include <phonon/streaminterface.h> + + +#define WAVEHEADER_OFFSET_FORMATTAG 20 +#define WAVEHEADER_OFFSET_CHANNELS 22 +#define WAVEHEADER_OFFSET_SAMPLESPERSEC 24 +#define WAVEHEADER_OFFSET_AVGBYTESPERSEC 28 +#define WAVEHEADER_OFFSET_BLOCKALIGN 32 +#define WAVEHEADER_OFFSET_BITSPERSAMPLE 34 +#define WAVEHEADER_OFFSET_DATA 44 +#define WAVEHEADER_SIZE WAVEHEADER_OFFSET_DATA + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + static unsigned int buffer_size = (16 * 1024 * 4); + + QString getErrorText(MMRESULT error) + { + ushort b[256]; + waveOutGetErrorText(error, (LPWSTR)b, 256); + return QString::fromUtf16(b); + } + + class WorkerThread : public QThread + { + Q_OBJECT + public slots: + void stream(QIODevice *file, QByteArray *buffer, bool *finished); + }; + + void WorkerThread::stream(QIODevice *ioStream, QByteArray *buffer, bool *finished) + { + (*finished) = false; + memset((void*) buffer->data(), 0, buffer->size()); + qint64 i = ioStream->read(buffer->data(), buffer_size); + buffer->resize(i); + (*finished) = true; + } + + + void CALLBACK MediaObject::WaveOutCallBack(HWAVEOUT m_hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) + { + Q_UNUSED(m_hWaveOut); + Q_UNUSED(dwInstance); + Q_UNUSED(dwParam2); + + switch(uMsg) + { + case WOM_OPEN: + break; + case WOM_DONE: + { + WAVEHDR *waveHeader = (WAVEHDR*)dwParam1; + MediaObject* mediaObject = reinterpret_cast<MediaObject *>(waveHeader->dwUser); + if (mediaObject) { + mediaObject->swapBuffers(); + } + } + break; + case WOM_CLOSE: + break; + } + } + + class StreamReader : public Phonon::StreamInterface + { + public: + StreamReader(QObject *parent, const Phonon::MediaSource &source) : + m_seekable(false), m_pos(0), m_size(-1) + { + Q_UNUSED(parent); + connectToSource(source); + } + + //for Phonon::StreamInterface + void writeData(const QByteArray &data) + { + QWriteLocker locker(&m_lock); + m_pos += data.size(); + m_buffer += data; + } + + void endOfData() + { + } + + void setStreamSize(qint64 newSize) + { + QWriteLocker locker(&m_lock); + m_size = newSize; + } + + qint64 streamSize() const + { + QReadLocker locker(&m_lock); + return m_size; + } + + void setStreamSeekable(bool s) + { + QWriteLocker locker(&m_lock); + m_seekable = s; + } + + bool streamSeekable() const + { + QReadLocker locker(&m_lock); + return m_seekable; + } + + void setCurrentPos(qint64 pos) + { + QWriteLocker locker(&m_lock); + m_pos = pos; + seekStream(pos); + m_buffer.clear(); + } + + qint64 currentPos() const + { + QReadLocker locker(&m_lock); + return m_pos; + } + + int currentBufferSize() const + { + QReadLocker locker(&m_lock); + return m_buffer.size(); + } + + //for Phonon::StreamInterface + QByteArray m_buffer; + bool m_seekable; + qint64 m_pos; + qint64 m_size; + mutable QReadWriteLock m_lock; + }; + + class IOWrapper : public QIODevice { + public: + IOWrapper(QObject *parent, const Phonon::MediaSource &source) : m_streamReader(this, source) + { + Q_UNUSED(parent); + setOpenMode(QIODevice::ReadOnly); + } + bool seek(qint64 pos); + qint64 size() const; + qint64 pos(); + bool isReadable() const; + protected: + qint64 readData (char * data, qint64 maxSize); + qint64 writeData(const char *,qint64); + private: + StreamReader m_streamReader; + }; + + bool IOWrapper::isReadable () const + { + return true; + } + + qint64 IOWrapper::pos() + { + return (m_streamReader.streamSeekable() ? m_streamReader.currentPos() : 0); + } + + bool IOWrapper::seek( qint64 pos) + { + if (!m_streamReader.streamSeekable()) + return false; + m_streamReader.setCurrentPos(pos); + return true; + } + + qint64 IOWrapper::size() const + { + return m_streamReader.streamSize(); + } + + qint64 IOWrapper::readData(char * data, qint64 maxSize) + { + int oldSize = m_streamReader.currentBufferSize(); + while (m_streamReader.currentBufferSize() < maxSize) { + m_streamReader.needData(); + if (oldSize == m_streamReader.currentBufferSize()) { + break; //we didn't get any data + } + oldSize = m_streamReader.currentBufferSize(); + } + + qint64 bytesRead = qMin(qint64(m_streamReader.currentBufferSize()), maxSize); + { + QWriteLocker locker(&m_streamReader.m_lock); + qMemCopy(data, m_streamReader.m_buffer.data(), bytesRead); + //truncate the buffer + m_streamReader.m_buffer = m_streamReader.m_buffer.mid(bytesRead); + } + return bytesRead; + } + + qint64 IOWrapper::writeData(const char *,qint64) + { + return 0; + } + + MediaObject::MediaObject(QObject *parent) : m_file(0), m_stream(0), + m_hWaveOut(0), m_nextBufferIndex(1), + m_mediaSize(-1), m_bufferingFinished(0), + m_paused(0), m_tickInterval(0), + m_hasNextSource(0), m_hasSource(0), + m_sourceIsValid(0), m_errorType(Phonon::NoError), + m_currentTime(0), m_transitionTime(0), + m_tick(0), m_volume(100), m_prefinishMark(0), + m_tickIntervalResolution(0), m_bufferPrepared(0), + m_stopped(0) + { + m_thread = new WorkerThread(); + connect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*))); + m_thread->start(); + m_soundBuffer1.waveHeader = new WAVEHDR; + m_soundBuffer2.waveHeader = new WAVEHDR; + setParent(parent); + setState(Phonon::LoadingState); + } + + MediaObject::~MediaObject() + { + stop(); + disconnect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*))); + do { //The event loop of m_thread might not be started, yet + m_thread->quit(); //If the event loop is not started yet quit() does nothing + m_thread->wait(100); + } while (m_thread->isRunning()); + delete m_thread; + deleteValidWaveOutDevice(); + delete m_soundBuffer1.waveHeader; + delete m_soundBuffer2.waveHeader; + } + + Phonon::State MediaObject::state() const + { + return m_state; + } + + bool MediaObject::hasVideo() const + { + return false; + } + + bool MediaObject::isSeekable() const + { + if (!m_stream) + return false; + return !m_stream->isSequential(); + } + + qint64 MediaObject::totalTime() const + { + return m_totalTime; + } + + qint64 MediaObject::currentTime() const + { + //this handles inaccuracy when stopping on a title + return m_currentTime; + } + + qint32 MediaObject::tickInterval() const + { + return m_tickInterval * m_tickIntervalResolution; + } + + void MediaObject::setTickInterval(qint32 newTickInterval) + { + if ((m_tickIntervalResolution == 0) || (newTickInterval == 0)) + return; + m_tickInterval = newTickInterval / m_tickIntervalResolution; + if ((newTickInterval > 0) && (m_tickInterval == 0)) + m_tickInterval = 1; + } + + void MediaObject::pause() + { + if (!m_paused) { + m_paused = true; + setState(Phonon::PausedState); + if (!(waveOutPause(m_hWaveOut) == MMSYSERR_NOERROR)) + { + setError(Phonon::NormalError, QLatin1String("cannot pause (system error)")); + } + } + } + + void MediaObject::stop() + { + setState(Phonon::StoppedState); + m_stopped = true; + m_paused = false; + seek(0); + if (!(waveOutReset(m_hWaveOut) == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot stop (system error)")); + } + + void MediaObject::play() + { + if ((m_state == Phonon::PlayingState) && !m_paused && !m_stopped) + return; + if ((m_state == Phonon::LoadingState) || + (m_state == Phonon::BufferingState) || + (m_state == Phonon::ErrorState)) { + setError(Phonon::FatalError, QLatin1String("illegale state for playback")); + return; + } + + if (m_state == Phonon::StoppedState) + stop(); + if (m_sourceIsValid) { + setState(Phonon::PlayingState); + if (!m_paused) { + m_nextBufferIndex = true; + m_stopped = false; + playBuffer(m_soundBuffer1.waveHeader); + playBuffer(m_soundBuffer2.waveHeader); + } else { + if (!(waveOutRestart(m_hWaveOut) == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot resume (system)")); + } + } else { + setError(Phonon::FatalError, QLatin1String("cannot playback invalid source")); + } + m_paused = false; + } + + QString MediaObject::errorString() const + { + + return m_errorString; + } + + Phonon::ErrorType MediaObject::errorType() const + { + return Phonon::ErrorType(); + } + + qint32 MediaObject::prefinishMark() const + { + return m_prefinishMark; + } + + void MediaObject::setPrefinishMark(qint32 newPrefinishMark) + { + m_prefinishMark = newPrefinishMark; + } + + qint32 MediaObject::transitionTime() const + { + return m_transitionTime; + } + + void MediaObject::setTransitionTime(qint32 time) + { + m_transitionTime = time; + } + + qint64 MediaObject::remainingTime() const + { + return m_totalTime - m_currentTime; + } + + Phonon::MediaSource MediaObject::source() const + { + return Phonon::MediaSource(); + } + + void MediaObject::setNextSource(const Phonon::MediaSource &source) + { + m_nextSource = source; + m_hasNextSource = true; + } + + void MediaObject::setSource(const Phonon::MediaSource &source) + { + if (m_state == Phonon::PlayingState) + { + setError(Phonon::NormalError, QLatin1String("source changed while playing")); + stop(); + } + + m_source = source; + m_hasSource = true; + m_sourceIsValid = false; + + emit currentSourceChanged(source); + + if (source.type() == Phonon::MediaSource::LocalFile) { + if (!openWaveFile(source.fileName())) { + setError(Phonon::FatalError, QLatin1String("cannot open media file")); + return ; + } + } else if (source.type() == Phonon::MediaSource::Stream) { + if (m_stream) + delete m_stream; + m_stream = new IOWrapper(this, source); + m_mediaSize = m_stream->size(); + } else if (source.type() == Phonon::MediaSource::Url) { + if (!openWaveFile(source.url().toLocalFile())) { + setError(Phonon::FatalError, QLatin1String("cannot open media file")); + return ; + } + } else { + setError(Phonon::FatalError, QLatin1String("type of source not supported")); + return ; + } + setState(Phonon::LoadingState); + + if (!readHeader()) + setError(Phonon::FatalError, QLatin1String("invalid header")); + else if (!getWaveOutDevice()) + setError(Phonon::FatalError, QLatin1String("No waveOut device available")); + else if (!fillBuffers()) + setError(Phonon::FatalError, QLatin1String("no data for buffering")); + else if (!prepareBuffers()) + setError(Phonon::FatalError, QLatin1String("cannot prepare buffers")); + else + m_sourceIsValid = true; + + if (m_sourceIsValid) + setState(Phonon::StoppedState); + } + + void MediaObject::seek(qint64 time) + { + if (!m_sourceIsValid) { + setError(Phonon::NormalError, QLatin1String("source is not valid")); + return; + } + if ((time >= 0) && (time < m_totalTime)) { + int counter = 0; + while (!m_bufferingFinished && (counter < 200)) { + Sleep(20); + counter ++; + } + if (counter >= 200) { + setError(Phonon::NormalError, QLatin1String("buffering timed out")); + return; + } + + m_stream->seek(WAVEHEADER_SIZE + time * m_waveFormatEx.nSamplesPerSec * m_waveFormatEx.wBitsPerSample * m_waveFormatEx.nChannels / 8 / 1000); + m_currentTime = time; + if (m_state == Phonon::PlayingState) + play(); + } else { + setError(Phonon::NormalError, QLatin1String("seeking out of range")); + } + } + + void MediaObject::unPrepareBuffers() + { + if (m_bufferPrepared) { + DWORD err1 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR)); + DWORD err2 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR)); + if (!(err1 == MMSYSERR_NOERROR) || !(err2 == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot unprepare buffer") + getErrorText(err1) + getErrorText(err2)); + } + m_bufferPrepared = false; + } + + bool MediaObject::prepareBuffers() + { + memset((void*)m_soundBuffer1.waveHeader, 0, sizeof(WAVEHDR)); + m_soundBuffer1.waveHeader->lpData = m_soundBuffer1.data.data(); + m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size(); + m_soundBuffer1.waveHeader->dwUser = (DWORD_PTR) this; + + ZeroMemory((void*)m_soundBuffer2.waveHeader, sizeof(WAVEHDR)); + m_soundBuffer2.waveHeader->lpData = m_soundBuffer2.data.data(); + m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer1.data.size(); + m_soundBuffer2.waveHeader->dwUser = (DWORD_PTR) this; + + m_bufferPrepared = (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) + && (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR); + return m_bufferPrepared; + } + + void MediaObject::deleteValidWaveOutDevice() + { + if (m_hWaveOut) { + unPrepareBuffers(); + if (!(waveOutClose(m_hWaveOut) == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot close wave device")); + } + } + + bool MediaObject::getWaveOutDevice() + { + deleteValidWaveOutDevice(); + + for(UINT deviceId = 0; deviceId < waveOutGetNumDevs(); deviceId++) + { + if(deviceId == waveOutGetNumDevs()) + return false; + if(waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_waveFormatEx, (DWORD)WaveOutCallBack, 0, CALLBACK_FUNCTION) == MMSYSERR_NOERROR) + return m_hWaveOut; //m_hWaveOut !=0; + } + return false; + } + + bool MediaObject::openWaveFile(QString fileName) + { + if (m_file) + delete m_file; + m_file = new QFile(fileName); + m_file->setParent(this); + m_stream = m_file; + m_mediaSize = m_file->size(); + return (m_file->open(QIODevice::ReadOnly)); + } + + bool MediaObject::readHeader() + { + QByteArray header = m_stream->read(WAVEHEADER_SIZE); + + if (header.size() == WAVEHEADER_SIZE) { + + m_waveFormatEx.wFormatTag = *((WORD* )(header.data() + WAVEHEADER_OFFSET_FORMATTAG )); + m_waveFormatEx.nChannels = *((WORD* )(header.data() + WAVEHEADER_OFFSET_CHANNELS )); + m_waveFormatEx.nSamplesPerSec = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_SAMPLESPERSEC )); + m_waveFormatEx.nAvgBytesPerSec = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_AVGBYTESPERSEC)); + m_waveFormatEx.nBlockAlign = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BLOCKALIGN )); + m_waveFormatEx.wBitsPerSample = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BITSPERSAMPLE )); + + m_tickIntervalResolution = (qint64(buffer_size) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels; + if (m_mediaSize > 0) + m_totalTime = ((m_mediaSize - WAVEHEADER_SIZE) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels; + else + m_totalTime = -1; + emit totalTimeChanged(m_totalTime); + return true; + } else { + return false; + } + } + + bool MediaObject::fillBuffers() + { + + m_soundBuffer1.data = m_stream->read(buffer_size); + m_soundBuffer2.data = m_stream->read(buffer_size); + + m_bufferingFinished = true; + + if (!(m_soundBuffer1.data.size() > 0)) + setError(Phonon::NormalError, QLatin1String("cannot read source")); + return true; + } + + void MediaObject::setState(Phonon::State newState) + { + if (m_state == newState) + return; + emit stateChanged(newState, m_state); + m_state = newState; + } + + void MediaObject::setError(ErrorType errorType, QString errorMessage) + { + m_errorType = errorType; + setState(Phonon::ErrorState); + m_errorString = errorMessage; + } + + void MediaObject::setAudioOutput(QObject *audioOutput) + { + m_audioOutput = qobject_cast<AudioOutput*>(audioOutput); + + if (m_audioOutput) { + m_volume = m_audioOutput->volume(); + connect(m_audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(setVolume(qreal))); + } + } + + void MediaObject::setVolume(qreal newVolume) + { + m_volume = newVolume; + } + + void MediaObject::swapBuffers() + { + if (m_stopped || m_paused) + return; + + m_currentTime += m_tickIntervalResolution; + if (m_tickInterval) { + m_tick ++; + if (m_tick > (m_tickInterval - 1)) { + emit tick(m_currentTime); + m_tick = 0; + } + } + if ((m_prefinishMark > 0)&& (m_prefinishMark < m_currentTime)) + emit prefinishMarkReached(m_totalTime - m_currentTime); + + while (!m_bufferingFinished) { + setState(Phonon::BufferingState); + qWarning() << QLatin1String("buffer underun"); + Sleep(20); + } + + setState(Phonon::PlayingState); + + //if size == o then stop... + if (m_nextBufferIndex) { + int size = m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size(); + if (size == buffer_size) { + playBuffer(m_soundBuffer1.waveHeader); + emit outOfData(m_stream, &m_soundBuffer1.data, &m_bufferingFinished); + } else { + playBuffer(m_soundBuffer1.waveHeader); + m_stopped = true; + setState(Phonon::StoppedState); + emit finished(); + seek(0); + } + } else { + int size = m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer2.data.size(); + if (size == buffer_size) { + playBuffer(m_soundBuffer2.waveHeader); + emit outOfData(m_stream, &m_soundBuffer2.data, &m_bufferingFinished); + } else { + playBuffer(m_soundBuffer2.waveHeader); + m_stopped = true; + setState(Phonon::StoppedState); + emit finished(); + seek(0); + } + } + m_nextBufferIndex =! m_nextBufferIndex; + } + + + void MediaObject::playBuffer(WAVEHDR *waveHeader) + { + DWORD err = waveOutWrite(m_hWaveOut, waveHeader, sizeof(WAVEHDR)); + if (!err == MMSYSERR_NOERROR) { + setError(Phonon::FatalError, QLatin1String("cannot play sound buffer (system) ") + getErrorText(err)); + m_stopped = true; + } + } + } +} + +QT_END_NAMESPACE + +#include "mediaobject.moc" diff --git a/src/3rdparty/phonon/waveout/mediaobject.h b/src/3rdparty/phonon/waveout/mediaobject.h new file mode 100644 index 0000000..dd6b24b --- /dev/null +++ b/src/3rdparty/phonon/waveout/mediaobject.h @@ -0,0 +1,162 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +This library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 or 3 of the License. + +This library 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_MEDIAOBJECT_H +#define PHONON_MEDIAOBJECT_H + +#include <phonon/mediaobjectinterface.h> + +#include <QtCore/QHash> +#include <QtCore/QObject> +#include <QtCore/QQueue> +#include <QtCore/QBasicTimer> +#include <QtCore/QWaitCondition> +#include <QtCore/QMutex> +#include <QtCore/QThread> +#include <QFile> +#include <QIODevice> + +#include <windows.h> + + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class MediaSource; + + namespace WaveOut + { + class WorkerThread; + class AudioOutput; + + class MediaObject : public QObject, public Phonon::MediaObjectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface) + + public: + MediaObject(QObject *parent); + ~MediaObject(); + Phonon::State state() const; + bool hasVideo() const; + bool isSeekable() const; + qint64 currentTime() const; + qint32 tickInterval() const; + + void setTickInterval(qint32 newTickInterval); + void play(); + void pause(); + void stop(); + void seek(qint64 time); + + QString errorString() const; + Phonon::ErrorType errorType() const; + qint64 totalTime() const; + qint32 prefinishMark() const; + void setPrefinishMark(qint32 newPrefinishMark); + qint32 transitionTime() const; + void setTransitionTime(qint32); + qint64 remainingTime() const; + MediaSource source() const; + void setSource(const MediaSource &source); + void setNextSource(const MediaSource &source); + + + Q_SIGNALS: + void stateChanged(Phonon::State newstate, Phonon::State oldstate); + void tick(qint64 time); + void metaDataChanged(QMultiMap<QString, QString>); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + void finished(); + void prefinishMarkReached(qint32); + void aboutToFinish(); + void totalTimeChanged(qint64 length) const; + void currentSourceChanged(const MediaSource &); + void outOfData(QIODevice *ioStream, QByteArray *buffer, bool *m_bufferingFinshed); + + protected: + void setAudioOutput(QObject *audioOutput); + + private Q_SLOTS: + void setVolume(qreal newVolume); + + private: + bool m_nextBufferIndex; + bool prepareBuffers(); + void unPrepareBuffers(); + bool getWaveOutDevice(); + bool openWaveFile(QString fileName); + bool readHeader(); + bool boolUpdateBuffer(); + bool fillBuffers(); + void swapBuffers(); + void setState(Phonon::State newState); + void setError(ErrorType errorType, QString errorMessage); + void deleteValidWaveOutDevice(); + void playBuffer(WAVEHDR *waveHeader); + + static void CALLBACK WaveOutCallBack(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + + struct { + WAVEHDR *waveHeader; + QByteArray data; + } m_soundBuffer1, m_soundBuffer2; + + WAVEFORMATEX m_waveFormatEx; + HWAVEOUT m_hWaveOut; + + QFile *m_file; + QIODevice *m_stream; + QString m_errorString; + + WorkerThread *m_thread; + + MediaSource m_source; + MediaSource m_nextSource; + AudioOutput *m_audioOutput; + ErrorType m_errorType; + + qreal m_volume; + qint64 m_mediaSize; + qint64 m_totalTime; + qint64 m_currentTime; + qint64 m_transitionTime; + qint64 m_prefinishMark; + qint64 m_tickIntervalResolution; + qint32 m_tickInterval; + qint32 m_tick; + Phonon::State m_state; + + bool m_bufferingFinished; + bool m_paused; + bool m_stopped; + bool m_hasNextSource; + bool m_hasSource; + bool m_sourceIsValid; + bool m_bufferPrepared; + + friend class Backend; + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_MEDIAOBJECT_H |