src/3rdparty/phonon/waveout/mediaobject.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5 This library is free software: you can redistribute it and/or modify
       
     6 it under the terms of the GNU Lesser General Public License as published by
       
     7 the Free Software Foundation, either version 2.1 or 3 of the License.
       
     8 
       
     9 This library is distributed in the hope that it will be useful,
       
    10 but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 GNU Lesser General Public License for more details.
       
    13 
       
    14 You should have received a copy of the GNU Lesser General Public License
       
    15 along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 #include "mediaobject.h"
       
    19 #include "audiooutput.h"
       
    20 
       
    21 #include <QtCore/QVector>
       
    22 #include <QtCore/QTimerEvent>
       
    23 #include <QtCore/QTimer>
       
    24 #include <QtCore/QTime>
       
    25 #include <QtCore/QLibrary>
       
    26 #include <QtCore/QUrl>
       
    27 #include <QtCore/QWriteLocker>
       
    28 
       
    29 #include <phonon/streaminterface.h>
       
    30 
       
    31 
       
    32 #define WAVEHEADER_OFFSET_FORMATTAG        20
       
    33 #define WAVEHEADER_OFFSET_CHANNELS         22
       
    34 #define WAVEHEADER_OFFSET_SAMPLESPERSEC    24
       
    35 #define WAVEHEADER_OFFSET_AVGBYTESPERSEC   28
       
    36 #define WAVEHEADER_OFFSET_BLOCKALIGN       32
       
    37 #define WAVEHEADER_OFFSET_BITSPERSAMPLE    34
       
    38 #define WAVEHEADER_OFFSET_DATA             44
       
    39 #define WAVEHEADER_SIZE                    WAVEHEADER_OFFSET_DATA
       
    40 
       
    41 QT_BEGIN_NAMESPACE
       
    42 
       
    43 namespace Phonon
       
    44 {
       
    45     namespace WaveOut
       
    46     {        
       
    47         static unsigned int buffer_size = (16 * 1024 * 4);
       
    48 
       
    49         QString getErrorText(MMRESULT error)
       
    50         {
       
    51             ushort b[256];
       
    52             waveOutGetErrorText(error, (LPWSTR)b, 256);
       
    53             return QString::fromUtf16(b);
       
    54         }
       
    55 
       
    56         class WorkerThread : public QThread
       
    57         {
       
    58          Q_OBJECT
       
    59          public slots:
       
    60               void stream(QIODevice *file, QByteArray *buffer, bool *finished);
       
    61         };
       
    62 
       
    63         void WorkerThread::stream(QIODevice *ioStream, QByteArray *buffer, bool *finished)
       
    64         {
       
    65             (*finished) = false;
       
    66             memset((void*) buffer->data(), 0, buffer->size());
       
    67             qint64 i = ioStream->read(buffer->data(), buffer_size);
       
    68             buffer->resize(i);
       
    69             (*finished) = true;
       
    70         }
       
    71 
       
    72 
       
    73         void CALLBACK MediaObject::WaveOutCallBack(HWAVEOUT m_hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
       
    74         {
       
    75             Q_UNUSED(m_hWaveOut);
       
    76             Q_UNUSED(dwInstance);
       
    77             Q_UNUSED(dwParam2);
       
    78 
       
    79             switch(uMsg)
       
    80             {
       
    81             case WOM_OPEN:
       
    82                 break;
       
    83             case WOM_DONE:
       
    84                 {
       
    85                     WAVEHDR *waveHeader = (WAVEHDR*)dwParam1;
       
    86                     MediaObject* mediaObject = reinterpret_cast<MediaObject *>(waveHeader->dwUser);
       
    87                     if (mediaObject) {
       
    88                         mediaObject->swapBuffers();
       
    89                     }
       
    90                 }
       
    91                 break;
       
    92             case WOM_CLOSE:
       
    93                 break;
       
    94             }
       
    95         }
       
    96 
       
    97         class StreamReader : public Phonon::StreamInterface
       
    98         {
       
    99         public:
       
   100               StreamReader(QObject *parent, const Phonon::MediaSource &source) :
       
   101                   m_seekable(false), m_pos(0), m_size(-1)
       
   102               {
       
   103                   Q_UNUSED(parent);
       
   104                   connectToSource(source);
       
   105               }
       
   106 
       
   107               //for Phonon::StreamInterface
       
   108               void writeData(const QByteArray &data)
       
   109               {
       
   110                   QWriteLocker locker(&m_lock);
       
   111                   m_pos += data.size();
       
   112                   m_buffer += data;
       
   113               }
       
   114 
       
   115               void endOfData()
       
   116               {
       
   117               }
       
   118 
       
   119               void setStreamSize(qint64 newSize)
       
   120               {
       
   121                   QWriteLocker locker(&m_lock);
       
   122                   m_size = newSize;
       
   123               }
       
   124 
       
   125               qint64 streamSize() const
       
   126               {
       
   127                   QReadLocker locker(&m_lock);
       
   128                   return m_size;
       
   129               }
       
   130 
       
   131               void setStreamSeekable(bool s)
       
   132               {
       
   133                   QWriteLocker locker(&m_lock);
       
   134                   m_seekable = s;
       
   135               }
       
   136 
       
   137               bool streamSeekable() const
       
   138               {
       
   139                   QReadLocker locker(&m_lock);
       
   140                   return m_seekable;
       
   141               }
       
   142 
       
   143               void setCurrentPos(qint64 pos)
       
   144               {
       
   145                   QWriteLocker locker(&m_lock);
       
   146                   m_pos = pos;
       
   147                   seekStream(pos);
       
   148                   m_buffer.clear();
       
   149               }
       
   150 
       
   151               qint64 currentPos() const
       
   152               {
       
   153                   QReadLocker locker(&m_lock);
       
   154                   return m_pos;
       
   155               }
       
   156 
       
   157               int currentBufferSize() const
       
   158               {
       
   159                   QReadLocker locker(&m_lock);
       
   160                   return m_buffer.size();
       
   161               }
       
   162 
       
   163             //for Phonon::StreamInterface
       
   164             QByteArray m_buffer;
       
   165             bool m_seekable;
       
   166             qint64 m_pos;
       
   167             qint64 m_size;
       
   168             mutable QReadWriteLock m_lock;
       
   169         };
       
   170 
       
   171         class IOWrapper : public QIODevice {
       
   172         public:
       
   173             IOWrapper(QObject *parent, const Phonon::MediaSource &source) : m_streamReader(this, source)
       
   174             {
       
   175                 Q_UNUSED(parent);
       
   176                 setOpenMode(QIODevice::ReadOnly);
       
   177             }
       
   178             bool seek(qint64 pos);
       
   179             qint64 size() const;
       
   180             qint64 pos();
       
   181             bool isReadable() const;
       
   182          protected:
       
   183             qint64 readData (char * data, qint64 maxSize);
       
   184             qint64 writeData(const char *,qint64);
       
   185         private:
       
   186             StreamReader m_streamReader;
       
   187         };
       
   188 
       
   189         bool IOWrapper::isReadable () const
       
   190         {
       
   191             return true;
       
   192         }
       
   193 
       
   194         qint64 IOWrapper::pos()
       
   195         {
       
   196             return (m_streamReader.streamSeekable() ? m_streamReader.currentPos() : 0);
       
   197         }
       
   198 
       
   199         bool IOWrapper::seek( qint64 pos)
       
   200         {
       
   201             if (!m_streamReader.streamSeekable())
       
   202                 return false;
       
   203             m_streamReader.setCurrentPos(pos);
       
   204             return true;
       
   205         }
       
   206 
       
   207         qint64 IOWrapper::size() const
       
   208         {
       
   209             return m_streamReader.streamSize();
       
   210         }
       
   211 
       
   212         qint64 IOWrapper::readData(char * data, qint64 maxSize)
       
   213         {
       
   214             int oldSize = m_streamReader.currentBufferSize();
       
   215             while (m_streamReader.currentBufferSize() < maxSize) {
       
   216                 m_streamReader.needData();
       
   217                 if (oldSize == m_streamReader.currentBufferSize()) {
       
   218                     break; //we didn't get any data
       
   219                 }
       
   220                 oldSize = m_streamReader.currentBufferSize();
       
   221             }
       
   222 
       
   223             qint64 bytesRead = qMin(qint64(m_streamReader.currentBufferSize()), maxSize);
       
   224             {
       
   225                 QWriteLocker locker(&m_streamReader.m_lock);
       
   226                 qMemCopy(data, m_streamReader.m_buffer.data(), bytesRead);
       
   227                 //truncate the buffer
       
   228                 m_streamReader.m_buffer = m_streamReader.m_buffer.mid(bytesRead);
       
   229             }
       
   230             return bytesRead;
       
   231         }
       
   232 
       
   233         qint64 IOWrapper::writeData(const char *,qint64)
       
   234         {
       
   235             return 0;
       
   236         }
       
   237 
       
   238         MediaObject::MediaObject(QObject *parent) : m_file(0), m_stream(0),
       
   239                                                     m_hWaveOut(0), m_nextBufferIndex(1), 
       
   240                                                     m_mediaSize(-1), m_bufferingFinished(0),
       
   241                                                     m_paused(0), m_tickInterval(0),
       
   242                                                     m_hasNextSource(0), m_hasSource(0),
       
   243                                                     m_sourceIsValid(0), m_errorType(Phonon::NoError),
       
   244                                                     m_currentTime(0), m_transitionTime(0),
       
   245                                                     m_tick(0), m_volume(100), m_prefinishMark(0),
       
   246                                                     m_tickIntervalResolution(0), m_bufferPrepared(0),
       
   247                                                     m_stopped(0)
       
   248         {
       
   249             m_thread = new WorkerThread();
       
   250             connect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*)));
       
   251             m_thread->start();
       
   252             m_soundBuffer1.waveHeader = new WAVEHDR;
       
   253             m_soundBuffer2.waveHeader = new WAVEHDR;
       
   254             setParent(parent);
       
   255             setState(Phonon::LoadingState);            
       
   256         }
       
   257 
       
   258         MediaObject::~MediaObject()
       
   259         {
       
   260             stop();
       
   261             disconnect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*)));
       
   262             do { //The event loop of m_thread might not be started, yet
       
   263                 m_thread->quit(); //If the event loop is not started yet quit() does nothing
       
   264                 m_thread->wait(100);
       
   265             } while (m_thread->isRunning());
       
   266             delete m_thread;
       
   267             deleteValidWaveOutDevice();
       
   268             delete m_soundBuffer1.waveHeader;
       
   269             delete m_soundBuffer2.waveHeader;
       
   270         }
       
   271 
       
   272         Phonon::State MediaObject::state() const
       
   273         {
       
   274            return m_state;
       
   275         }
       
   276 
       
   277         bool MediaObject::hasVideo() const
       
   278         {
       
   279             return false;
       
   280         }
       
   281 
       
   282         bool MediaObject::isSeekable() const
       
   283         {
       
   284             if (!m_stream) 
       
   285               return false; 
       
   286             return !m_stream->isSequential();
       
   287         }
       
   288 
       
   289         qint64 MediaObject::totalTime() const
       
   290         {
       
   291             return m_totalTime;
       
   292         }
       
   293 
       
   294         qint64 MediaObject::currentTime() const
       
   295         {
       
   296             //this handles inaccuracy when stopping on a title
       
   297             return m_currentTime;
       
   298         }
       
   299 
       
   300         qint32 MediaObject::tickInterval() const
       
   301         {
       
   302             return m_tickInterval * m_tickIntervalResolution;
       
   303         }
       
   304 
       
   305         void MediaObject::setTickInterval(qint32 newTickInterval)
       
   306         {
       
   307             if ((m_tickIntervalResolution == 0) || (newTickInterval == 0))
       
   308                 return;
       
   309             m_tickInterval = newTickInterval / m_tickIntervalResolution;
       
   310             if ((newTickInterval > 0) && (m_tickInterval == 0))
       
   311                 m_tickInterval = 1;
       
   312         }
       
   313 
       
   314         void MediaObject::pause()
       
   315         {
       
   316             if (!m_paused) {
       
   317                 m_paused = true;
       
   318                 setState(Phonon::PausedState);
       
   319                 if (!(waveOutPause(m_hWaveOut) == MMSYSERR_NOERROR))
       
   320                 {
       
   321                     setError(Phonon::NormalError, QLatin1String("cannot pause (system error)"));
       
   322                 }
       
   323             }
       
   324         }
       
   325 
       
   326         void MediaObject::stop()
       
   327         {
       
   328             setState(Phonon::StoppedState);
       
   329             m_stopped = true;
       
   330             m_paused = false;
       
   331             seek(0);
       
   332             if (!(waveOutReset(m_hWaveOut) == MMSYSERR_NOERROR))
       
   333                 setError(Phonon::NormalError, QLatin1String("cannot stop (system error)"));
       
   334         }
       
   335 
       
   336         void MediaObject::play()
       
   337         {
       
   338             if ((m_state == Phonon::PlayingState) && !m_paused && !m_stopped)
       
   339                 return;
       
   340             if  ((m_state == Phonon::LoadingState) ||
       
   341                  (m_state == Phonon::BufferingState) ||
       
   342                  (m_state == Phonon::ErrorState)) {
       
   343                     setError(Phonon::FatalError, QLatin1String("illegale state for playback"));
       
   344                     return;
       
   345             }
       
   346 
       
   347             if (m_state == Phonon::StoppedState)
       
   348                 stop();
       
   349             if (m_sourceIsValid) {
       
   350                 setState(Phonon::PlayingState);
       
   351                 if (!m_paused) {
       
   352                     m_nextBufferIndex = true;
       
   353                     m_stopped = false;
       
   354                     playBuffer(m_soundBuffer1.waveHeader);
       
   355                     playBuffer(m_soundBuffer2.waveHeader);
       
   356                 } else {
       
   357                     if (!(waveOutRestart(m_hWaveOut) == MMSYSERR_NOERROR))
       
   358                         setError(Phonon::NormalError, QLatin1String("cannot resume (system)"));
       
   359                 }
       
   360             } else {
       
   361                 setError(Phonon::FatalError, QLatin1String("cannot playback invalid source"));
       
   362             }
       
   363             m_paused = false;
       
   364         }
       
   365 
       
   366         QString MediaObject::errorString() const
       
   367         {
       
   368             
       
   369             return m_errorString;
       
   370         }
       
   371 
       
   372         Phonon::ErrorType MediaObject::errorType() const
       
   373         {
       
   374             return Phonon::ErrorType();
       
   375         }
       
   376 
       
   377         qint32 MediaObject::prefinishMark() const
       
   378         {
       
   379             return m_prefinishMark;
       
   380         }
       
   381 
       
   382         void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
       
   383         {
       
   384             m_prefinishMark = newPrefinishMark;
       
   385         }
       
   386 
       
   387         qint32 MediaObject::transitionTime() const
       
   388         {
       
   389             return m_transitionTime;
       
   390         }
       
   391 
       
   392         void MediaObject::setTransitionTime(qint32 time)
       
   393         {
       
   394            m_transitionTime = time;
       
   395         }
       
   396 
       
   397         qint64 MediaObject::remainingTime() const
       
   398         {
       
   399             return m_totalTime - m_currentTime;
       
   400         }
       
   401 
       
   402         Phonon::MediaSource MediaObject::source() const
       
   403         {
       
   404             return Phonon::MediaSource();
       
   405         }
       
   406 
       
   407         void MediaObject::setNextSource(const Phonon::MediaSource &source)
       
   408         {
       
   409             m_nextSource = source;
       
   410             m_hasNextSource = true;
       
   411         }
       
   412 
       
   413         void MediaObject::setSource(const Phonon::MediaSource &source)
       
   414         {
       
   415             if (m_state == Phonon::PlayingState)
       
   416             {
       
   417                 setError(Phonon::NormalError, QLatin1String("source changed while playing"));
       
   418                 stop();
       
   419             }
       
   420 
       
   421             m_source = source;
       
   422             m_hasSource = true;
       
   423             m_sourceIsValid = false;
       
   424 
       
   425             emit currentSourceChanged(source);
       
   426 
       
   427             if (source.type() == Phonon::MediaSource::LocalFile) {
       
   428                 if (!openWaveFile(source.fileName())) {
       
   429                   setError(Phonon::FatalError, QLatin1String("cannot open media file"));
       
   430                   return ;
       
   431                 }
       
   432             } else if (source.type() == Phonon::MediaSource::Stream) {
       
   433                 if (m_stream)
       
   434                    delete m_stream;
       
   435                 m_stream = new IOWrapper(this, source);
       
   436                 m_mediaSize = m_stream->size();
       
   437             } else if (source.type() == Phonon::MediaSource::Url) {
       
   438                 if (!openWaveFile(source.url().toLocalFile())) {
       
   439                     setError(Phonon::FatalError, QLatin1String("cannot open media file"));
       
   440                     return ;
       
   441                 }
       
   442             } else {
       
   443                 setError(Phonon::FatalError, QLatin1String("type of source not supported"));
       
   444                 return ;
       
   445             }
       
   446             setState(Phonon::LoadingState);
       
   447 
       
   448             if (!readHeader())
       
   449                 setError(Phonon::FatalError, QLatin1String("invalid header"));
       
   450             else if (!getWaveOutDevice())
       
   451                 setError(Phonon::FatalError, QLatin1String("No waveOut device available"));
       
   452             else if (!fillBuffers())
       
   453                 setError(Phonon::FatalError, QLatin1String("no data for buffering"));
       
   454             else if (!prepareBuffers())
       
   455                 setError(Phonon::FatalError, QLatin1String("cannot prepare buffers"));
       
   456             else
       
   457                 m_sourceIsValid = true;
       
   458 
       
   459             if (m_sourceIsValid)
       
   460                 setState(Phonon::StoppedState);
       
   461         }
       
   462 
       
   463         void MediaObject::seek(qint64 time)
       
   464         {
       
   465             if (!m_sourceIsValid) {
       
   466                 setError(Phonon::NormalError, QLatin1String("source is not valid"));
       
   467                 return;
       
   468             }
       
   469             if ((time >= 0) && (time < m_totalTime)) {
       
   470                 int counter = 0;
       
   471                 while (!m_bufferingFinished && (counter < 200)) {
       
   472                   Sleep(20);
       
   473                   counter ++;
       
   474                 }
       
   475                 if (counter >= 200) {
       
   476                    setError(Phonon::NormalError, QLatin1String("buffering timed out"));
       
   477                    return;
       
   478                 }
       
   479 
       
   480                 m_stream->seek(WAVEHEADER_SIZE + time * m_waveFormatEx.nSamplesPerSec * m_waveFormatEx.wBitsPerSample * m_waveFormatEx.nChannels / 8 / 1000);
       
   481                 m_currentTime = time;
       
   482                 if (m_state == Phonon::PlayingState)
       
   483                   play();
       
   484             } else {
       
   485                 setError(Phonon::NormalError, QLatin1String("seeking out of range"));
       
   486             }
       
   487         }
       
   488 
       
   489         void MediaObject::unPrepareBuffers()
       
   490         {
       
   491             if (m_bufferPrepared) {
       
   492             DWORD err1 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR));
       
   493             DWORD err2 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR));
       
   494             if (!(err1 == MMSYSERR_NOERROR) || !(err2 == MMSYSERR_NOERROR))
       
   495                 setError(Phonon::NormalError, QLatin1String("cannot unprepare buffer") + getErrorText(err1) + getErrorText(err2));
       
   496             }
       
   497             m_bufferPrepared = false;
       
   498         }
       
   499 
       
   500         bool MediaObject::prepareBuffers()
       
   501         {
       
   502             memset((void*)m_soundBuffer1.waveHeader, 0, sizeof(WAVEHDR));
       
   503             m_soundBuffer1.waveHeader->lpData = m_soundBuffer1.data.data();
       
   504             m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size();
       
   505             m_soundBuffer1.waveHeader->dwUser = (DWORD_PTR) this;
       
   506 
       
   507             ZeroMemory((void*)m_soundBuffer2.waveHeader, sizeof(WAVEHDR));
       
   508             m_soundBuffer2.waveHeader->lpData = m_soundBuffer2.data.data();
       
   509             m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer1.data.size();
       
   510             m_soundBuffer2.waveHeader->dwUser = (DWORD_PTR) this;
       
   511 
       
   512             m_bufferPrepared = (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR)
       
   513                 && (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR);
       
   514              return m_bufferPrepared;
       
   515         }
       
   516 
       
   517         void MediaObject::deleteValidWaveOutDevice()
       
   518         {
       
   519             if (m_hWaveOut) {
       
   520                 unPrepareBuffers();
       
   521                 if (!(waveOutClose(m_hWaveOut)  == MMSYSERR_NOERROR))
       
   522                     setError(Phonon::NormalError, QLatin1String("cannot close wave device"));
       
   523             }
       
   524         }
       
   525 
       
   526         bool MediaObject::getWaveOutDevice()
       
   527         {
       
   528             deleteValidWaveOutDevice();
       
   529 
       
   530             for(UINT deviceId = 0; deviceId < waveOutGetNumDevs(); deviceId++)
       
   531             {
       
   532                 if(deviceId == waveOutGetNumDevs())
       
   533                     return false;
       
   534                 if(waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_waveFormatEx, (DWORD)WaveOutCallBack, 0, CALLBACK_FUNCTION) == MMSYSERR_NOERROR)
       
   535                     return m_hWaveOut; //m_hWaveOut !=0;
       
   536             }
       
   537             return false;
       
   538         }
       
   539 
       
   540         bool  MediaObject::openWaveFile(QString fileName)
       
   541         {
       
   542             if (m_file)
       
   543                 delete m_file;
       
   544             m_file = new QFile(fileName);
       
   545             m_file->setParent(this);
       
   546             m_stream = m_file;
       
   547             m_mediaSize = m_file->size();
       
   548             return (m_file->open(QIODevice::ReadOnly));
       
   549         }
       
   550 
       
   551         bool MediaObject::readHeader()
       
   552         {
       
   553             QByteArray header = m_stream->read(WAVEHEADER_SIZE);
       
   554 
       
   555             if (header.size() == WAVEHEADER_SIZE) {
       
   556 
       
   557                 m_waveFormatEx.wFormatTag         = *((WORD* )(header.data() + WAVEHEADER_OFFSET_FORMATTAG     ));
       
   558                 m_waveFormatEx.nChannels          = *((WORD* )(header.data() + WAVEHEADER_OFFSET_CHANNELS      ));
       
   559                 m_waveFormatEx.nSamplesPerSec     = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_SAMPLESPERSEC ));
       
   560                 m_waveFormatEx.nAvgBytesPerSec    = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_AVGBYTESPERSEC));
       
   561                 m_waveFormatEx.nBlockAlign        = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BLOCKALIGN    ));
       
   562                 m_waveFormatEx.wBitsPerSample     = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BITSPERSAMPLE ));
       
   563 
       
   564                 m_tickIntervalResolution = (qint64(buffer_size) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels;
       
   565                 if (m_mediaSize > 0)
       
   566                    m_totalTime = ((m_mediaSize - WAVEHEADER_SIZE) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels;
       
   567                 else
       
   568                   m_totalTime = -1;
       
   569                   emit totalTimeChanged(m_totalTime);
       
   570                 return true;
       
   571             } else {
       
   572                 return false;
       
   573             }
       
   574         }
       
   575         
       
   576         bool MediaObject::fillBuffers()
       
   577         {
       
   578             
       
   579             m_soundBuffer1.data = m_stream->read(buffer_size);
       
   580             m_soundBuffer2.data = m_stream->read(buffer_size);
       
   581 
       
   582             m_bufferingFinished = true;
       
   583 
       
   584             if (!(m_soundBuffer1.data.size() > 0))
       
   585                 setError(Phonon::NormalError, QLatin1String("cannot read source"));
       
   586             return true;
       
   587         }
       
   588 
       
   589         void MediaObject::setState(Phonon::State newState)
       
   590         {
       
   591             if (m_state == newState)
       
   592                 return;
       
   593             emit stateChanged(newState, m_state);
       
   594             m_state = newState;
       
   595         }
       
   596 
       
   597         void MediaObject::setError(ErrorType errorType, QString errorMessage)
       
   598         {
       
   599             m_errorType = errorType;
       
   600             setState(Phonon::ErrorState);
       
   601             m_errorString = errorMessage;
       
   602         }
       
   603 
       
   604         void MediaObject::setAudioOutput(QObject *audioOutput)
       
   605         {
       
   606             m_audioOutput = qobject_cast<AudioOutput*>(audioOutput);
       
   607 
       
   608             if (m_audioOutput) {
       
   609                 m_volume = m_audioOutput->volume();
       
   610                 connect(m_audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(setVolume(qreal)));
       
   611             }
       
   612         }
       
   613 
       
   614         void MediaObject::setVolume(qreal newVolume)
       
   615         {
       
   616             m_volume = newVolume;
       
   617         }
       
   618 
       
   619         void MediaObject::swapBuffers()
       
   620         {
       
   621             if (m_stopped || m_paused)
       
   622                 return;
       
   623 
       
   624             m_currentTime += m_tickIntervalResolution;
       
   625             if (m_tickInterval) {
       
   626                 m_tick ++;
       
   627                 if (m_tick > (m_tickInterval - 1)) {
       
   628                     emit tick(m_currentTime);
       
   629                     m_tick = 0;
       
   630                 }
       
   631             }
       
   632             if ((m_prefinishMark > 0)&& (m_prefinishMark < m_currentTime))
       
   633                 emit prefinishMarkReached(m_totalTime - m_currentTime);
       
   634 
       
   635             while (!m_bufferingFinished) {
       
   636                 setState(Phonon::BufferingState);
       
   637                 qWarning() << QLatin1String("buffer underun");
       
   638                 Sleep(20);
       
   639             }
       
   640 
       
   641             setState(Phonon::PlayingState);
       
   642 
       
   643             //if size == o then stop...
       
   644             if (m_nextBufferIndex) {
       
   645                 int size = m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size();
       
   646                 if (size == buffer_size) {
       
   647                     playBuffer(m_soundBuffer1.waveHeader);
       
   648                     emit outOfData(m_stream, &m_soundBuffer1.data, &m_bufferingFinished);
       
   649                 } else {
       
   650                     playBuffer(m_soundBuffer1.waveHeader);
       
   651                     m_stopped = true;
       
   652                     setState(Phonon::StoppedState);
       
   653                     emit finished();
       
   654                     seek(0);
       
   655                 }
       
   656             } else {
       
   657                 int size = m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer2.data.size();
       
   658                 if (size == buffer_size) {
       
   659                     playBuffer(m_soundBuffer2.waveHeader);
       
   660                     emit outOfData(m_stream, &m_soundBuffer2.data, &m_bufferingFinished);
       
   661                 } else {
       
   662                     playBuffer(m_soundBuffer2.waveHeader);
       
   663                     m_stopped = true;
       
   664                     setState(Phonon::StoppedState);
       
   665                     emit finished();
       
   666                     seek(0);
       
   667                 }
       
   668             }
       
   669             m_nextBufferIndex =! m_nextBufferIndex;
       
   670         }
       
   671 
       
   672 
       
   673         void MediaObject::playBuffer(WAVEHDR *waveHeader)
       
   674         {
       
   675             DWORD err = waveOutWrite(m_hWaveOut, waveHeader, sizeof(WAVEHDR));
       
   676             if (!err == MMSYSERR_NOERROR) {
       
   677                 setError(Phonon::FatalError, QLatin1String("cannot play sound buffer (system) ") + getErrorText(err));
       
   678                 m_stopped = true;
       
   679             }
       
   680         }
       
   681     }
       
   682 }
       
   683 
       
   684 QT_END_NAMESPACE
       
   685 
       
   686 #include "mediaobject.moc"