source: trunk/src/gui/kernel/qsound_pm.cpp@ 564

Last change on this file since 564 was 564, checked in by Dmitry A. Kuminov, 15 years ago

general: Updated headers of OS/2 sources.

File size: 28.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2009 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qsound.h"
45
46#ifndef QT_NO_SOUND
47
48#include "qapplication.h"
49#include "qapplication_p.h"
50#include <qfile.h>
51#include "qsound_p.h"
52
53#include "qlist.h"
54#include "qhash.h"
55#include "qlibrary.h"
56
57#include <qt_os2.h>
58
59#include <stdlib.h> // for getenv()
60
61//#define QT_QSOUND_DEBUG
62
63// local QT_DEBUG override, must be placed *after* all includes
64#if defined(QT_QSOUND_DEBUG) && !defined(QT_DEBUG)
65# define QT_DEBUG
66#endif
67
68QT_BEGIN_NAMESPACE
69
70////////////////////////////////////////////////////////////////////////////////
71
72//#define INCL_MCIOS2
73//#define INCL_MMIOOS2
74//#include <os2me.h>
75
76// The below definitions are stolen from the OS/2 Toolkit 4.5 headers
77// to avoid the requirement of having the Toolkit installed when building Qt
78// and let it directly link to mdm.dll and mmio.dll.
79
80typedef ULONG HMMIO; /* Handle to an MMIO object*/
81
82typedef ULONG FOURCC;
83
84typedef LONG (APIENTRY MMIOPROC) /* Format must */
85 (PVOID pmmioinfo, /* appear this */
86 USHORT usMsg, /* way for h2inc */
87 LONG lParam1, /* to work properly. */
88 LONG lParam2);
89
90typedef MMIOPROC FAR *PMMIOPROC;
91
92typedef struct _MMIOINFO { /* mmioinfo */
93 ULONG ulFlags; /* Open flags */
94 FOURCC fccIOProc; /* FOURCC of the IOProc to use */
95 PMMIOPROC pIOProc; /* Function Pointer to IOProc to use */
96 ULONG ulErrorRet; /* Extended Error return code */
97 LONG cchBuffer; /* I/O buff size (if used), Fsize if MEM */
98 PCHAR pchBuffer; /* Start of I/O buff */
99 PCHAR pchNext; /* Next char to read or write in buff */
100 PCHAR pchEndRead; /* Last char in buff can be read + 1 */
101 PCHAR pchEndWrite; /* Last char in buff can be written + 1 */
102 LONG lBufOffset; /* Offset in buff to pchNext */
103 LONG lDiskOffset; /* Disk offset in file */
104 ULONG aulInfo[4]; /* IOProc specific fields */
105 LONG lLogicalFilePos; /* Actual file position, buffered or not */
106 ULONG ulTranslate; /* Translation field */
107 FOURCC fccChildIOProc; /* FOURCC of Child IOProc */
108 PVOID pExtraInfoStruct; /* Pointer to a structure of related data */
109 HMMIO hmmio; /* Handle to media element */
110 } MMIOINFO;
111
112typedef MMIOINFO FAR *PMMIOINFO;
113
114typedef struct _WAVE_HEADER { /* waveheader */
115 USHORT usFormatTag; /* Type of wave format */
116 USHORT usChannels; /* Number of channels */
117 ULONG ulSamplesPerSec; /* Sampling rate */
118 ULONG ulAvgBytesPerSec; /* Avg bytes per sec */
119 USHORT usBlockAlign; /* Block Alignment in bytes */
120 USHORT usBitsPerSample; /* Bits per sample */
121 } WAVE_HEADER;
122
123typedef struct _XWAV_HEADERINFO { /* xwaveheader info */
124 ULONG ulAudioLengthInMS; /* Audio data in millisecs */
125 ULONG ulAudioLengthInBytes; /* Audio data in bytes */
126 PVOID pAdditionalInformation;
127 } XWAV_HEADERINFO;
128
129typedef struct _MMXWAV_HEADER { /* mmxwaveheader */
130 WAVE_HEADER WAVEHeader; /* Per RIFF WAVE Definition */
131 XWAV_HEADERINFO XWAVHeaderInfo; /* Extended wave definition */
132 } MMXWAV_HEADER;
133
134typedef struct _MMAUDIOHEADER { /* mmaudioheader */
135 ULONG ulHeaderLength; /* Length in Bytes */
136 ULONG ulContentType; /* Image content */
137 ULONG ulMediaType; /* Media Type */
138 MMXWAV_HEADER mmXWAVHeader; /* header */
139 } MMAUDIOHEADER;
140
141typedef MMAUDIOHEADER *PMMAUDIOHEADER;
142
143typedef struct _MCI_OPEN_PARMS
144{
145 HWND hwndCallback; /* PM window handle for MCI notify message */
146 USHORT usDeviceID; /* Device ID returned to user */
147 USHORT usReserved0; /* Reserved */
148 PSZ pszDeviceType; /* Device name from SYSTEM.INI */
149 PSZ pszElementName; /* Typically a file name or NULL */
150 PSZ pszAlias; /* Optional device alias */
151} MCI_OPEN_PARMS;
152typedef MCI_OPEN_PARMS *PMCI_OPEN_PARMS;
153
154typedef struct _MCI_PLAY_PARMS
155{
156 HWND hwndCallback; /* PM window handle for MCI notify message */
157 ULONG ulFrom; /* Play from this position */
158 ULONG ulTo; /* Play to this position */
159} MCI_PLAY_PARMS;
160typedef MCI_PLAY_PARMS *PMCI_PLAY_PARMS;
161
162#define MMIO_TRANSLATEDATA 0x00000001L /* Translation */
163#define MMIO_TRANSLATEHEADER 0x00000002L /* Translation */
164
165#define MMIO_READ 0x00000004L /* Open */
166
167#define MMIO_SUCCESS 0L
168
169#define MMIO_MEDIATYPE_AUDIO 0x00000002L /* Audio media */
170
171#define MCI_DEVTYPE_WAVEFORM_AUDIO 7
172
173#define MCI_NOTIFY 0x00000001L
174#define MCI_WAIT 0x00000002L
175#define MCI_FROM 0x00000004L
176
177#define MCI_OPEN_TYPE_ID 0x00001000L
178#define MCI_OPEN_SHAREABLE 0x00002000L
179#define MCI_OPEN_MMIO 0x00004000L
180#define MCI_READONLY 0x00008000L
181
182#define MCI_RETURN_RESOURCE 0x00000100L
183
184#define MCI_CLOSE_EXIT 0x10000000L
185
186#define MCI_OPEN 1
187#define MCI_CLOSE 2
188#define MCI_PLAY 4
189#define MCI_STOP 6
190#define MCI_ACQUIREDEVICE 23
191#define MCI_RELEASEDEVICE 24
192
193#define MM_MCINOTIFY 0x0500
194#define MM_MCIPASSDEVICE 0x0501
195
196#define MCI_NOTIFY_SUCCESSFUL 0x0000
197#define MCI_NOTIFY_SUPERSEDED 0x0001
198
199#define MCI_LOSING_USE 0x00000001L
200#define MCI_GAINING_USE 0x00000002L
201
202#define MCIERR_BASE 5000
203#define MCIERR_SUCCESS 0
204#define MCIERR_INSTANCE_INACTIVE (MCIERR_BASE + 34)
205#define MCIERR_DEVICE_LOCKED (MCIERR_BASE + 32)
206
207// functions resolved by mmio.dll
208
209typedef
210USHORT (APIENTRY *mmioClose_T)(HMMIO hmmio,
211 USHORT usFlags);
212typedef
213HMMIO (APIENTRY *mmioOpen_T)(PSZ pszFileName,
214 PMMIOINFO pmmioinfo,
215 ULONG ulOpenFlags);
216
217typedef
218ULONG (APIENTRY *mmioGetHeader_T)(HMMIO hmmio,
219 PVOID pHeader,
220 LONG lHeaderLength,
221 PLONG plBytesRead,
222 ULONG ulReserved,
223 ULONG ulFlags);
224
225static mmioClose_T mmioClose = 0;
226static mmioOpen_T mmioOpen = 0;
227static mmioGetHeader_T mmioGetHeader = 0;
228
229// functions resolved by mdm.dll
230
231typedef
232ULONG (APIENTRY *mciSendCommand_T)(USHORT usDeviceID,
233 USHORT usMessage,
234 ULONG ulParam1,
235 PVOID pParam2,
236 USHORT usUserParm);
237
238static mciSendCommand_T mciSendCommand = 0;
239
240////////////////////////////////////////////////////////////////////////////////
241
242class QAuBucketMMPM;
243
244class QAuServerMMPM : public QAuServer {
245
246 Q_OBJECT
247
248public:
249
250 QAuServerMMPM(QObject* parent);
251 ~QAuServerMMPM();
252
253 void init(QSound *s);
254 void play(const QString &filename);
255 void play(QSound *s);
256 void stop(QSound *s);
257 bool okay();
258
259private slots:
260
261 void serverUninit();
262
263private:
264
265 HWND hwnd;
266 bool isOk;
267
268 QLibrary mdmLib;
269 QLibrary mmioLib;
270
271 QHash<int, QAuBucketMMPM *> bucketMap;
272 QList<QAuBucketMMPM *> bucketList;
273
274 static const char *ClassName;
275 static MRESULT EXPENTRY WindowProc(HWND hwnd, ULONG msg,
276 MPARAM mp1, MPARAM mp2);
277
278 friend class QAuBucketMMPM;
279};
280
281////////////////////////////////////////////////////////////////////////////////
282
283class QAuBucketMMPM : public QAuBucket {
284
285public:
286
287 enum State { Stopped, Playing, Waiting };
288
289 QAuBucketMMPM(QAuServerMMPM *server, QSound *sound);
290 QAuBucketMMPM(QAuServerMMPM *server, const QString &soundFile);
291 ~QAuBucketMMPM();
292
293 void open();
294 void close(bool atExit = false);
295 bool play();
296 void stop();
297 bool okay() { return fileHandle != NULLHANDLE; }
298
299 QSound *sound() { return snd; }
300 State state() { return st; }
301
302 void onDeviceGained(bool gained);
303
304#if defined(QT_DEBUG)
305 QByteArray fileName() { return fName; }
306#endif
307
308private:
309
310 void init(const QString &fileName);
311
312 QAuServerMMPM *srv;
313 QSound *snd;
314
315#if defined(QT_DEBUG)
316 QByteArray fName;
317#endif
318
319 HMMIO fileHandle;
320 USHORT deviceId;
321
322 State st;
323};
324
325////////////////////////////////////////////////////////////////////////////////
326
327QAuBucketMMPM::QAuBucketMMPM(QAuServerMMPM *server, QSound *sound) :
328 srv(server), snd(sound), fileHandle(NULLHANDLE), deviceId(0),
329 st(Stopped)
330{
331 Q_ASSERT(srv);
332 Q_ASSERT(snd);
333 if (!srv || !snd)
334 return;
335
336 init(snd->fileName());
337}
338
339QAuBucketMMPM::QAuBucketMMPM(QAuServerMMPM *server, const QString &fileName) :
340 srv(server), snd(NULL), fileHandle(NULLHANDLE), deviceId(0),
341 st(Stopped)
342{
343 Q_ASSERT(srv);
344 if (!srv)
345 return;
346
347 init(fileName);
348}
349
350void QAuBucketMMPM::init(const QString &soundFile)
351{
352 Q_ASSERT(fileHandle == NULLHANDLE);
353 if (fileHandle != NULLHANDLE)
354 return;
355
356#if !defined(QT_DEBUG)
357 QByteArray
358#endif
359 fName = QFile::encodeName(soundFile);
360
361 MMIOINFO mmioinfo = { 0 };
362 mmioinfo.ulTranslate = MMIO_TRANSLATEDATA | MMIO_TRANSLATEHEADER;
363 fileHandle = mmioOpen(fName.data(), &mmioinfo, MMIO_READ);
364 if (fileHandle == NULLHANDLE) {
365#if defined(QT_DEBUG)
366 qDebug("QAuBucketMMPM: falied to open sound file [%s]", fName.data());
367#endif
368 return;
369 }
370
371 MMAUDIOHEADER mmah;
372 LONG bytesRead = 0;
373 ULONG rc = mmioGetHeader(fileHandle, &mmah, sizeof(mmah), &bytesRead, 0, 0);
374 if (rc != MMIO_SUCCESS || mmah.ulMediaType != MMIO_MEDIATYPE_AUDIO) {
375#if defined(QT_DEBUG)
376 qDebug("QAuBucketMMPM: [%s] is not a sound file or "
377 "has an unsupported format (rc=%04hu:%04hu)",
378 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
379#endif
380 mmioClose(fileHandle, 0);
381 fileHandle = NULLHANDLE;
382 return;
383 }
384
385 srv->bucketList.append(this);
386
387#if defined(QT_QSOUND_DEBUG)
388 qDebug("QAuBucketMMPM::init(): {%p} [%s]", this, fName.data());
389#endif
390}
391
392QAuBucketMMPM::~QAuBucketMMPM()
393{
394#if defined(QT_QSOUND_DEBUG)
395 qDebug("~QAuBucketMMPM(): {%p} [%s]", this, fName.data());
396#endif
397
398 if (deviceId)
399 close(srv->hwnd == NULLHANDLE);
400
401 if (fileHandle != NULLHANDLE) {
402 // removeo from the list unless called from serverUninit()
403 if (srv->hwnd != NULLHANDLE)
404 srv->bucketList.removeAll(this);
405 mmioClose(fileHandle, 0);
406 }
407}
408
409void QAuBucketMMPM::open()
410{
411 Q_ASSERT(!deviceId);
412 if (deviceId)
413 return;
414
415 MCI_OPEN_PARMS openParams = { 0 };
416 openParams.hwndCallback = srv->hwnd;
417 openParams.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 0);
418 openParams.pszElementName = (PSZ) fileHandle;
419 ULONG params = MCI_WAIT | MCI_OPEN_MMIO | MCI_READONLY | MCI_OPEN_TYPE_ID;
420 if (getenv("QT_PM_NO_SOUND_SHARE") != NULL) {
421#if defined(QT_DEBUG)
422 qDebug("QAuBucketMMPM: WARNING: opening device for sound file [%s] in "
423 "exclusive mode due to QT_PM_NO_SOUND_SHARE=%s",
424 fName.data(), getenv("QT_PM_NO_SOUND_SHARE"));
425#endif
426 } else if (getenv("QT_PM_SOUND_SHARE") != NULL) {
427#if defined(QT_DEBUG)
428 qDebug("QAuBucketMMPM: WARNING: opening device for sound file [%s] in "
429 "shareable mode due to QT_PM_SOUND_SHARE=%s",
430 fName.data(), getenv("QT_PM_SOUND_SHARE"));
431#endif
432 params |= MCI_OPEN_SHAREABLE;
433 } else {
434 // Too pity, but we have to use exclusive mode by default due to
435 // problems with the uniaud driver that is most commonly used nowadays.
436 // In non-exclusive (shared) mode, we will hang up in mmpm.dll doing
437 // mciSendCommand(MCI_CLOSE) in close() for the last played sound if
438 // we start playing 10 or more sounds simultaneously (i.e. w/o waiting
439 // until the previous one completely finishes playing).
440 }
441 ULONG rc = mciSendCommand(0, MCI_OPEN, params, &openParams, 0);
442 if (rc != MCIERR_SUCCESS) {
443#if defined(QT_DEBUG)
444 qDebug("QAuBucketMMPM: failed to open a device for sound file [%s] "
445 "(rc=%04hu:%04hu)", fName.data(), HIUSHORT(rc), LOUSHORT(rc));
446#endif
447 return;
448 }
449
450 deviceId = openParams.usDeviceID;
451
452 srv->bucketMap.insert(deviceId, this);
453
454#if defined(QT_QSOUND_DEBUG)
455 qDebug("QAuBucketMMPM::open(): {%p} [%s] deviceId=%08hu",
456 this, fName.data(), deviceId);
457#endif
458}
459
460void QAuBucketMMPM::close(bool atExit /* = false */)
461{
462 Q_ASSERT(deviceId);
463 if (!deviceId)
464 return;
465
466#if defined(QT_QSOUND_DEBUG)
467 qDebug("QAuBucketMMPM::close(): {%p} [%s] atExit=%d",
468 this, fName.data(), atExit);
469#endif
470
471 // remove from the map anyway -- we don't plan to retry
472 srv->bucketMap.remove(deviceId);
473
474 // Use MCI_CLOSE_EXIT to tell the media control driver it should
475 // close immediately, w/o waiting or doing any notifications, etc.
476 ULONG param = atExit ? MCI_CLOSE_EXIT : 0;
477
478 ULONG rc = mciSendCommand(deviceId, MCI_CLOSE, param, NULL, 0);
479#if defined(QT_DEBUG)
480 if (rc != MCIERR_SUCCESS)
481 qDebug("QAuBucketMMPM: failed to close the device for sound file [%s] "
482 "(rc=%04hu:%04hu)",
483 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
484#endif
485 Q_UNUSED(rc);
486
487 st = Stopped;
488 deviceId = 0;
489}
490
491bool QAuBucketMMPM::play()
492{
493#if defined(QT_QSOUND_DEBUG)
494 qDebug("QAuBucketMMPM::play(): {%p} [%s]", this, fName.data());
495#endif
496
497 if (!deviceId) {
498 open();
499 if (!deviceId)
500 return false;
501 }
502
503 MCI_PLAY_PARMS playParams = { 0 };
504 playParams.hwndCallback = srv->hwnd;
505 playParams.ulFrom = 0; // always play from the beginning
506
507 ULONG rc = mciSendCommand(deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
508 &playParams, 0);
509
510 if (LOUSHORT(rc) == MCIERR_INSTANCE_INACTIVE) {
511 // There are not enough simultaneous audio streams. Try to acquire the
512 // resources and play again. Note that if the device is already acquired
513 // for exclusive use, this command will not wait but return immediately.
514 rc = mciSendCommand(deviceId, MCI_ACQUIREDEVICE, MCI_WAIT, NULL, 0);
515 if (rc == MCIERR_SUCCESS) {
516 rc = mciSendCommand(deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
517 &playParams, 0);
518 } else if (LOUSHORT(rc) == MCIERR_DEVICE_LOCKED &&
519 snd && snd->loops() < 0) {
520 // Enter the special state to let this infinitive sound start
521 // playing when the resource becomes available.
522 st = Waiting;
523 return false;
524 }
525 }
526
527 if (rc == MCIERR_SUCCESS) {
528 st = Playing;
529 } else {
530 st = Stopped;
531#if defined(QT_DEBUG)
532 qDebug("QAuBucketMMPM: failed to play sound file [%s] (rc=%04hu:%04hu)",
533 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
534#endif
535 }
536
537 return st == Playing;
538}
539
540void QAuBucketMMPM::stop()
541{
542 if (st == Stopped)
543 return;
544
545 // always go to Stopped -- we won't retry
546 // (this is also used in MM_MCINOTIFY processing)
547 st = Stopped;
548
549 ULONG rc = mciSendCommand(deviceId, MCI_STOP, MCI_WAIT, NULL, 0);
550
551 if (rc == MCIERR_SUCCESS) {
552 // nothing
553 } else if (LOUSHORT(rc) == MCIERR_INSTANCE_INACTIVE) {
554 // The infinite QSound-full bucket is now suspended (some other instance
555 // has gained the resource). Close this instance to prevent it from
556 // being automatically resumed later.
557 close();
558 } else {
559#if defined(QT_DEBUG)
560 qDebug("QAuBucketMMPM: failed to stop sound file [%s] (rc=%04hu:%04hu)",
561 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
562#endif
563 // last chance to stop
564 close();
565 }
566}
567
568void QAuBucketMMPM::onDeviceGained(bool gained)
569{
570 if (gained) {
571 // We gained the device resource.
572 if (st == Waiting) {
573 /// @todo (dmik) For some reason, starting playback from here
574 // (i.e. when we've been given the deivce resource back after
575 // some other application finished using it exclusively), we can
576 // get error 5053 (MCIERR_NO_CONNECTION) or even undocumented 5659.
577 // When it happens, subsequent attempts to play something will
578 // result into an unkillable hang... Experimemtal for now.
579#if 1
580 // A QSound-full bucket attempted to play eventually regained
581 // the resource. Start playing.
582 play();
583#else
584 st = Stopped;
585#endif
586 }
587 } else {
588 // We lost the device resource.
589 if (st == Playing) {
590 // Close the instance to prevent the playback from being resumed
591 // when the device is auto-gained again (by another instance that
592 // uses MCI_RETURN_RESOURCE in MCI_RELEASEDEVICE). The exception is
593 // a QSound-full bucket with an infinitive loop (that will continue
594 // playing when the resuorce is back).
595 if (snd) {
596 // infinitive loop?
597 if (snd->loops() < 0)
598 return;
599 // decrease loops to zero
600 while (srv->decLoop(snd) > 0) ;
601 }
602 close();
603 if (!snd) {
604 // delete QSound-less bucket
605 delete this;
606 }
607 }
608 }
609}
610
611////////////////////////////////////////////////////////////////////////////////
612
613const char *QAuServerMMPM::ClassName = "QAuServerMMPM";
614
615QAuServerMMPM::QAuServerMMPM(QObject* parent) :
616 QAuServer(parent),
617 hwnd(NULLHANDLE), isOk(false),
618 mdmLib(QLatin1String("mdm.dll")), mmioLib(QLatin1String("mmio.dll"))
619{
620 WinRegisterClass(0, ClassName, WindowProc, 0, sizeof(PVOID));
621
622 hwnd = WinCreateWindow(HWND_OBJECT, ClassName, NULL, 0, 0, 0, 0, 0,
623 NULL, HWND_BOTTOM, 0,
624 this, NULL);
625
626 if (hwnd == NULLHANDLE) {
627 qWarning("QAuServerMMPM: WinCreateWindow(HWND_OBJECT) "
628 "failed with 0x%08lX", WinGetLastError(0));
629 return;
630 }
631
632 // resolve functions
633 mmioClose = (mmioClose_T) mmioLib.resolve("mmioClose");
634 mmioOpen = (mmioOpen_T) mmioLib.resolve("mmioOpen");
635 mmioGetHeader = (mmioGetHeader_T) mmioLib.resolve("mmioGetHeader");
636 mciSendCommand = (mciSendCommand_T) mdmLib.resolve("mciSendCommand");
637 if (!mmioClose || !mmioGetHeader || !mmioOpen || !mciSendCommand) {
638 qWarning("QAuServerMMPM: failed to resolve MMPM system functions");
639 return;
640 }
641
642 // try to open the default waveaudio device to ensure it exists
643 MCI_OPEN_PARMS openParams = { 0 };
644 openParams.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 0);
645 ULONG rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT |
646 MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
647 &openParams, 0);
648 if (rc != MCIERR_SUCCESS) {
649#if defined(QT_DEBUG)
650 qDebug("QAuServerMMPM: failed to open the default waveaudio device "
651 "(rc=%04hu:%04hu)", HIUSHORT(rc), LOUSHORT(rc));
652#endif
653 return;
654 }
655
656 // close the device opened above
657 mciSendCommand(openParams.usDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0);
658
659 connect(qApp, SIGNAL(aboutToQuit()), SLOT(serverUninit()));
660
661 isOk = true;
662}
663
664QAuServerMMPM::~QAuServerMMPM()
665{
666 // still alive?
667 if (hwnd)
668 serverUninit();
669}
670
671void QAuServerMMPM::serverUninit()
672{
673#if defined(QT_QSOUND_DEBUG)
674 qDebug("QAuServerMMPM::serverUninit(): buckets left: %d",
675 bucketList.count());
676#endif
677
678 Q_ASSERT(hwnd);
679 if (hwnd == NULLHANDLE)
680 return;
681
682 WinDestroyWindow(hwnd);
683 hwnd = NULLHANDLE;
684
685 // Deassociate all remaining buckets from QSound objects and delete them
686 // (note that deleting a bucket will remove it from the list, which
687 // in turn will advance the current item to the next one, so current() is
688 // is used instead of next() in the loop).
689 foreach (QAuBucketMMPM *b, bucketList) {
690 QSound *s = b->sound();
691 if (s) {
692 // the below call will delete the associated bucket
693 setBucket (s, 0);
694 } else {
695 delete b;
696 }
697 }
698
699 bucketList.clear();
700
701 Q_ASSERT(bucketMap.count() == 0);
702
703 mmioClose = 0;
704 mmioOpen = 0;
705 mmioGetHeader = 0;
706 mciSendCommand = 0;
707}
708
709void QAuServerMMPM::init(QSound *s)
710{
711 Q_ASSERT(s);
712
713 if (!okay())
714 return;
715
716 QAuBucketMMPM *b = new QAuBucketMMPM(this, s);
717 if (b->okay()) {
718 setBucket(s, b);
719#if defined(QT_QSOUND_DEBUG)
720 qDebug("QAuServerMMPM::init(): bucket=%p [%s]",
721 b, b->fileName().data());
722#endif
723 } else {
724 setBucket(s, 0);
725 // b is deleted in setBucket()
726 }
727}
728
729void QAuServerMMPM::play(const QString &filename)
730{
731 if (!okay())
732 return;
733
734 QAuBucketMMPM *b = new QAuBucketMMPM(this, filename);
735 if (b->okay()) {
736#if defined(QT_QSOUND_DEBUG)
737 qDebug("play(QString): bucket=%p [%s]", b, b->fileName().data());
738#endif
739 if (b->play()) {
740 // b will be deleted in WindowProc() or in onDeviceGained()
741 } else {
742 delete b;
743 }
744 } else {
745 delete b;
746 }
747}
748
749void QAuServerMMPM::play(QSound *s)
750{
751 Q_ASSERT(s);
752
753 if (!okay() || !isRelevant(s)) {
754 // no MMPM is available or a wrong sound, just decrease loops to zero
755 while (decLoop(s) > 0) ;
756 return;
757 }
758
759 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >(bucket(s));
760 if (b) {
761#if defined(QT_QSOUND_DEBUG)
762 qDebug("play(QSound): bucket=%p [%s]", b, b->fileName().data());
763#endif
764 b->play();
765 } else {
766 // failed to create a bucket in init(), just decrease loops to zero
767 while (decLoop(s) > 0) ;
768 }
769}
770
771void QAuServerMMPM::stop(QSound *s)
772{
773 Q_ASSERT(s);
774
775 if (!okay() || !isRelevant(s))
776 return;
777
778 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >(bucket(s));
779 if (b)
780 b->stop();
781}
782
783bool QAuServerMMPM::okay()
784{
785 return isOk;
786}
787
788MRESULT EXPENTRY QAuServerMMPM::WindowProc(HWND hwnd, ULONG msg,
789 MPARAM mp1, MPARAM mp2)
790{
791 QAuServerMMPM * that =
792 static_cast< QAuServerMMPM * >(WinQueryWindowPtr(hwnd, 0));
793
794 switch (msg) {
795 case WM_CREATE: {
796 QAuServerMMPM * that = static_cast< QAuServerMMPM * >(mp1);
797 if (!that)
798 return (MRESULT) TRUE;
799 WinSetWindowPtr(hwnd, 0, that);
800 return (MRESULT) FALSE;
801 }
802 case WM_DESTROY: {
803 return 0;
804 }
805 case MM_MCINOTIFY: {
806 if (!that)
807 return 0;
808
809 USHORT code = SHORT1FROMMP(mp1);
810 USHORT deviceId = SHORT1FROMMP(mp2);
811 USHORT mcimsg = SHORT2FROMMP(mp2);
812#if defined(QT_QSOUND_DEBUG)
813 qDebug("MM_MCINOTIFY: code=0x%04hX, deviceId=%04hu, mcimsg=%04hu",
814 code, deviceId, mcimsg);
815#endif
816 Q_ASSERT(mcimsg == MCI_PLAY);
817 if (mcimsg != MCI_PLAY)
818 return 0;
819
820 QAuBucketMMPM *b = that->bucketMap.value(deviceId);
821 // There will be a late notification if the sound is
822 // playing when close() happens. Just return silently.
823 if (!b)
824 return 0;
825
826#if defined(QT_QSOUND_DEBUG)
827 qDebug("MM_MCINOTIFY: bucket=%p [%s]", b, b->fileName().data());
828#endif
829
830 QSound *sound = b->sound();
831 if (sound) {
832 bool returnResource = false;
833 if (b->state() == QAuBucketMMPM::Stopped) {
834 // It's possible that MCI_STOP is issued right after MCI_PLAY
835 // has successfilly finished and sent MM_MCINOTIFY but before
836 // we start it again here. Obey the STOP request.
837 returnResource = true;
838 } else if (code == MCI_NOTIFY_SUCCESSFUL) {
839 // play the sound until there are no loops left
840 int loopsLeft = that->decLoop(sound);
841 if (loopsLeft != 0)
842 b->play();
843 else
844 returnResource = true;
845 } else if (code != MCI_NOTIFY_SUPERSEDED) {
846 returnResource = true;
847 }
848 if (returnResource) {
849 // let infinitive sounds continue playing
850 mciSendCommand(deviceId, MCI_RELEASEDEVICE,
851 MCI_RETURN_RESOURCE, NULL, 0);
852 }
853 } else {
854 // delete QSound-less bucket when finished or stopped playing
855 // (closing the instance will return the resource)
856 delete b;
857 }
858
859 return 0;
860 }
861 case MM_MCIPASSDEVICE: {
862 if (!that)
863 return 0;
864
865 USHORT deviceId = SHORT1FROMMP(mp1);
866 USHORT event = SHORT1FROMMP(mp2);
867#if defined(QT_QSOUND_DEBUG)
868 qDebug("MM_MCIPASSDEVICE: deviceId=%04hu, event=0x%04hX",
869 deviceId, event);
870#endif
871 QAuBucketMMPM *b = that->bucketMap.value(deviceId);
872 if (!b)
873 return 0;
874
875#if defined(QT_QSOUND_DEBUG)
876 qDebug("MM_MCIPASSDEVICE: bucket=%p [%s]", b, b->fileName().data());
877#endif
878 // Note: this call may delete b
879 b->onDeviceGained(event == MCI_GAINING_USE);
880
881 return 0;
882 }
883 }
884
885 return 0;
886}
887
888QAuServer* qt_new_audio_server()
889{
890 return new QAuServerMMPM(qApp);
891}
892
893QT_END_NAMESPACE
894
895#include "qsound_pm.moc"
896
897#endif // QT_NO_SOUND
Note: See TracBrowser for help on using the repository browser.