/src/3rdparty/phonon/waveout/mediaobject.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 686 lines · 572 code · 93 blank · 21 comment · 108 complexity · c5df135c7b856f4795446cc4c960233a MD5 · raw file

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