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

Last change on this file was 949, checked in by Dmitry A. Kuminov, 14 years ago

OS/2: QSound: Use shared mode by default.

This makes sense since the Uniaud driver that made a lot of problems
before when it came to shared sound card usage, is now much more
stable.

File size: 27.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** Copyright (C) 2010 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 qt-info@nokia.com.
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 // use shared mode by default
435 params |= MCI_OPEN_SHAREABLE;
436 }
437 ULONG rc = mciSendCommand(0, MCI_OPEN, params, &openParams, 0);
438 if (rc != MCIERR_SUCCESS) {
439#if defined(QT_DEBUG)
440 qDebug("QAuBucketMMPM: failed to open a device for sound file [%s] "
441 "(rc=%04hu:%04hu)", fName.data(), HIUSHORT(rc), LOUSHORT(rc));
442#endif
443 return;
444 }
445
446 deviceId = openParams.usDeviceID;
447
448 srv->bucketMap.insert(deviceId, this);
449
450#if defined(QT_QSOUND_DEBUG)
451 qDebug("QAuBucketMMPM::open(): {%p} [%s] deviceId=%08hu",
452 this, fName.data(), deviceId);
453#endif
454}
455
456void QAuBucketMMPM::close(bool atExit /* = false */)
457{
458 Q_ASSERT(deviceId);
459 if (!deviceId)
460 return;
461
462#if defined(QT_QSOUND_DEBUG)
463 qDebug("QAuBucketMMPM::close(): {%p} [%s] atExit=%d",
464 this, fName.data(), atExit);
465#endif
466
467 // remove from the map anyway -- we don't plan to retry
468 srv->bucketMap.remove(deviceId);
469
470 // Use MCI_CLOSE_EXIT to tell the media control driver it should
471 // close immediately, w/o waiting or doing any notifications, etc.
472 ULONG param = atExit ? MCI_CLOSE_EXIT : 0;
473
474 ULONG rc = mciSendCommand(deviceId, MCI_CLOSE, param, NULL, 0);
475#if defined(QT_DEBUG)
476 if (rc != MCIERR_SUCCESS)
477 qDebug("QAuBucketMMPM: failed to close the device for sound file [%s] "
478 "(rc=%04hu:%04hu)",
479 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
480#endif
481 Q_UNUSED(rc);
482
483 st = Stopped;
484 deviceId = 0;
485}
486
487bool QAuBucketMMPM::play()
488{
489#if defined(QT_QSOUND_DEBUG)
490 qDebug("QAuBucketMMPM::play(): {%p} [%s]", this, fName.data());
491#endif
492
493 if (!deviceId) {
494 open();
495 if (!deviceId)
496 return false;
497 }
498
499 MCI_PLAY_PARMS playParams = { 0 };
500 playParams.hwndCallback = srv->hwnd;
501 playParams.ulFrom = 0; // always play from the beginning
502
503 ULONG rc = mciSendCommand(deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
504 &playParams, 0);
505
506 if (LOUSHORT(rc) == MCIERR_INSTANCE_INACTIVE) {
507 // There are not enough simultaneous audio streams. Try to acquire the
508 // resources and play again. Note that if the device is already acquired
509 // for exclusive use, this command will not wait but return immediately.
510 rc = mciSendCommand(deviceId, MCI_ACQUIREDEVICE, MCI_WAIT, NULL, 0);
511 if (rc == MCIERR_SUCCESS) {
512 rc = mciSendCommand(deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
513 &playParams, 0);
514 } else if (LOUSHORT(rc) == MCIERR_DEVICE_LOCKED &&
515 snd && snd->loops() < 0) {
516 // Enter the special state to let this infinitive sound start
517 // playing when the resource becomes available.
518 st = Waiting;
519 return false;
520 }
521 }
522
523 if (rc == MCIERR_SUCCESS) {
524 st = Playing;
525 } else {
526 st = Stopped;
527#if defined(QT_DEBUG)
528 qDebug("QAuBucketMMPM: failed to play sound file [%s] (rc=%04hu:%04hu)",
529 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
530#endif
531 }
532
533 return st == Playing;
534}
535
536void QAuBucketMMPM::stop()
537{
538 if (st == Stopped)
539 return;
540
541 // always go to Stopped -- we won't retry
542 // (this is also used in MM_MCINOTIFY processing)
543 st = Stopped;
544
545 ULONG rc = mciSendCommand(deviceId, MCI_STOP, MCI_WAIT, NULL, 0);
546
547 if (rc == MCIERR_SUCCESS) {
548 // nothing
549 } else if (LOUSHORT(rc) == MCIERR_INSTANCE_INACTIVE) {
550 // The infinite QSound-full bucket is now suspended (some other instance
551 // has gained the resource). Close this instance to prevent it from
552 // being automatically resumed later.
553 close();
554 } else {
555#if defined(QT_DEBUG)
556 qDebug("QAuBucketMMPM: failed to stop sound file [%s] (rc=%04hu:%04hu)",
557 fName.data(), HIUSHORT(rc), LOUSHORT(rc));
558#endif
559 // last chance to stop
560 close();
561 }
562}
563
564void QAuBucketMMPM::onDeviceGained(bool gained)
565{
566 if (gained) {
567 // We gained the device resource.
568 if (st == Waiting) {
569 /// @todo (dmik) For some reason, starting playback from here
570 // (i.e. when we've been given the deivce resource back after
571 // some other application finished using it exclusively), we can
572 // get error 5053 (MCIERR_NO_CONNECTION) or even undocumented 5659.
573 // When it happens, subsequent attempts to play something will
574 // result into an unkillable hang... Experimemtal for now.
575#if 1
576 // A QSound-full bucket attempted to play eventually regained
577 // the resource. Start playing.
578 play();
579#else
580 st = Stopped;
581#endif
582 }
583 } else {
584 // We lost the device resource.
585 if (st == Playing) {
586 // Close the instance to prevent the playback from being resumed
587 // when the device is auto-gained again (by another instance that
588 // uses MCI_RETURN_RESOURCE in MCI_RELEASEDEVICE). The exception is
589 // a QSound-full bucket with an infinitive loop (that will continue
590 // playing when the resuorce is back).
591 if (snd) {
592 // infinitive loop?
593 if (snd->loops() < 0)
594 return;
595 // decrease loops to zero
596 while (srv->decLoop(snd) > 0) ;
597 }
598 close();
599 if (!snd) {
600 // delete QSound-less bucket
601 delete this;
602 }
603 }
604 }
605}
606
607////////////////////////////////////////////////////////////////////////////////
608
609const char *QAuServerMMPM::ClassName = "QAuServerMMPM";
610
611QAuServerMMPM::QAuServerMMPM(QObject* parent) :
612 QAuServer(parent),
613 hwnd(NULLHANDLE), isOk(false),
614 mdmLib(QLatin1String("mdm.dll")), mmioLib(QLatin1String("mmio.dll"))
615{
616 WinRegisterClass(0, ClassName, WindowProc, 0, sizeof(PVOID));
617
618 hwnd = WinCreateWindow(HWND_OBJECT, ClassName, NULL, 0, 0, 0, 0, 0,
619 NULL, HWND_BOTTOM, 0,
620 this, NULL);
621
622 if (hwnd == NULLHANDLE) {
623 qWarning("QAuServerMMPM: WinCreateWindow(HWND_OBJECT) "
624 "failed with 0x%08lX", WinGetLastError(0));
625 return;
626 }
627
628 // resolve functions
629 mmioClose = (mmioClose_T) mmioLib.resolve("mmioClose");
630 mmioOpen = (mmioOpen_T) mmioLib.resolve("mmioOpen");
631 mmioGetHeader = (mmioGetHeader_T) mmioLib.resolve("mmioGetHeader");
632 mciSendCommand = (mciSendCommand_T) mdmLib.resolve("mciSendCommand");
633 if (!mmioClose || !mmioGetHeader || !mmioOpen || !mciSendCommand) {
634 qWarning("QAuServerMMPM: failed to resolve MMPM system functions");
635 return;
636 }
637
638 // try to open the default waveaudio device to ensure it exists
639 MCI_OPEN_PARMS openParams = { 0 };
640 openParams.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 0);
641 ULONG rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT |
642 MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
643 &openParams, 0);
644 if (rc != MCIERR_SUCCESS) {
645#if defined(QT_DEBUG)
646 qDebug("QAuServerMMPM: failed to open the default waveaudio device "
647 "(rc=%04hu:%04hu)", HIUSHORT(rc), LOUSHORT(rc));
648#endif
649 return;
650 }
651
652 // close the device opened above
653 mciSendCommand(openParams.usDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0);
654
655 connect(qApp, SIGNAL(aboutToQuit()), SLOT(serverUninit()));
656
657 isOk = true;
658}
659
660QAuServerMMPM::~QAuServerMMPM()
661{
662 // still alive?
663 if (hwnd)
664 serverUninit();
665}
666
667void QAuServerMMPM::serverUninit()
668{
669#if defined(QT_QSOUND_DEBUG)
670 qDebug("QAuServerMMPM::serverUninit(): buckets left: %d",
671 bucketList.count());
672#endif
673
674 Q_ASSERT(hwnd);
675 if (hwnd == NULLHANDLE)
676 return;
677
678 WinDestroyWindow(hwnd);
679 hwnd = NULLHANDLE;
680
681 // Deassociate all remaining buckets from QSound objects and delete them
682 // (note that deleting a bucket will remove it from the list, which
683 // in turn will advance the current item to the next one, so current() is
684 // is used instead of next() in the loop).
685 foreach (QAuBucketMMPM *b, bucketList) {
686 QSound *s = b->sound();
687 if (s) {
688 // the below call will delete the associated bucket
689 setBucket (s, 0);
690 } else {
691 delete b;
692 }
693 }
694
695 bucketList.clear();
696
697 Q_ASSERT(bucketMap.count() == 0);
698
699 mmioClose = 0;
700 mmioOpen = 0;
701 mmioGetHeader = 0;
702 mciSendCommand = 0;
703}
704
705void QAuServerMMPM::init(QSound *s)
706{
707 Q_ASSERT(s);
708
709 if (!okay())
710 return;
711
712 QAuBucketMMPM *b = new QAuBucketMMPM(this, s);
713 if (b->okay()) {
714 setBucket(s, b);
715#if defined(QT_QSOUND_DEBUG)
716 qDebug("QAuServerMMPM::init(): bucket=%p [%s]",
717 b, b->fileName().data());
718#endif
719 } else {
720 setBucket(s, 0);
721 // b is deleted in setBucket()
722 }
723}
724
725void QAuServerMMPM::play(const QString &filename)
726{
727 if (!okay())
728 return;
729
730 QAuBucketMMPM *b = new QAuBucketMMPM(this, filename);
731 if (b->okay()) {
732#if defined(QT_QSOUND_DEBUG)
733 qDebug("play(QString): bucket=%p [%s]", b, b->fileName().data());
734#endif
735 if (b->play()) {
736 // b will be deleted in WindowProc() or in onDeviceGained()
737 } else {
738 delete b;
739 }
740 } else {
741 delete b;
742 }
743}
744
745void QAuServerMMPM::play(QSound *s)
746{
747 Q_ASSERT(s);
748
749 if (!okay() || !isRelevant(s)) {
750 // no MMPM is available or a wrong sound, just decrease loops to zero
751 while (decLoop(s) > 0) ;
752 return;
753 }
754
755 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >(bucket(s));
756 if (b) {
757#if defined(QT_QSOUND_DEBUG)
758 qDebug("play(QSound): bucket=%p [%s]", b, b->fileName().data());
759#endif
760 b->play();
761 } else {
762 // failed to create a bucket in init(), just decrease loops to zero
763 while (decLoop(s) > 0) ;
764 }
765}
766
767void QAuServerMMPM::stop(QSound *s)
768{
769 Q_ASSERT(s);
770
771 if (!okay() || !isRelevant(s))
772 return;
773
774 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >(bucket(s));
775 if (b)
776 b->stop();
777}
778
779bool QAuServerMMPM::okay()
780{
781 return isOk;
782}
783
784MRESULT EXPENTRY QAuServerMMPM::WindowProc(HWND hwnd, ULONG msg,
785 MPARAM mp1, MPARAM mp2)
786{
787 QAuServerMMPM * that =
788 static_cast< QAuServerMMPM * >(WinQueryWindowPtr(hwnd, 0));
789
790 switch (msg) {
791 case WM_CREATE: {
792 QAuServerMMPM * that = static_cast< QAuServerMMPM * >(mp1);
793 if (!that)
794 return (MRESULT) TRUE;
795 WinSetWindowPtr(hwnd, 0, that);
796 return (MRESULT) FALSE;
797 }
798 case WM_DESTROY: {
799 return 0;
800 }
801 case MM_MCINOTIFY: {
802 if (!that)
803 return 0;
804
805 USHORT code = SHORT1FROMMP(mp1);
806 USHORT deviceId = SHORT1FROMMP(mp2);
807 USHORT mcimsg = SHORT2FROMMP(mp2);
808#if defined(QT_QSOUND_DEBUG)
809 qDebug("MM_MCINOTIFY: code=0x%04hX, deviceId=%04hu, mcimsg=%04hu",
810 code, deviceId, mcimsg);
811#endif
812 Q_ASSERT(mcimsg == MCI_PLAY);
813 if (mcimsg != MCI_PLAY)
814 return 0;
815
816 QAuBucketMMPM *b = that->bucketMap.value(deviceId);
817 // There will be a late notification if the sound is
818 // playing when close() happens. Just return silently.
819 if (!b)
820 return 0;
821
822#if defined(QT_QSOUND_DEBUG)
823 qDebug("MM_MCINOTIFY: bucket=%p [%s]", b, b->fileName().data());
824#endif
825
826 QSound *sound = b->sound();
827 if (sound) {
828 bool returnResource = false;
829 if (b->state() == QAuBucketMMPM::Stopped) {
830 // It's possible that MCI_STOP is issued right after MCI_PLAY
831 // has successfilly finished and sent MM_MCINOTIFY but before
832 // we start it again here. Obey the STOP request.
833 returnResource = true;
834 } else if (code == MCI_NOTIFY_SUCCESSFUL) {
835 // play the sound until there are no loops left
836 int loopsLeft = that->decLoop(sound);
837 if (loopsLeft != 0)
838 b->play();
839 else
840 returnResource = true;
841 } else if (code != MCI_NOTIFY_SUPERSEDED) {
842 returnResource = true;
843 }
844 if (returnResource) {
845 // let infinitive sounds continue playing
846 mciSendCommand(deviceId, MCI_RELEASEDEVICE,
847 MCI_RETURN_RESOURCE, NULL, 0);
848 }
849 } else {
850 // delete QSound-less bucket when finished or stopped playing
851 // (closing the instance will return the resource)
852 delete b;
853 }
854
855 return 0;
856 }
857 case MM_MCIPASSDEVICE: {
858 if (!that)
859 return 0;
860
861 USHORT deviceId = SHORT1FROMMP(mp1);
862 USHORT event = SHORT1FROMMP(mp2);
863#if defined(QT_QSOUND_DEBUG)
864 qDebug("MM_MCIPASSDEVICE: deviceId=%04hu, event=0x%04hX",
865 deviceId, event);
866#endif
867 QAuBucketMMPM *b = that->bucketMap.value(deviceId);
868 if (!b)
869 return 0;
870
871#if defined(QT_QSOUND_DEBUG)
872 qDebug("MM_MCIPASSDEVICE: bucket=%p [%s]", b, b->fileName().data());
873#endif
874 // Note: this call may delete b
875 b->onDeviceGained(event == MCI_GAINING_USE);
876
877 return 0;
878 }
879 }
880
881 return 0;
882}
883
884QAuServer* qt_new_audio_server()
885{
886 return new QAuServerMMPM(qApp);
887}
888
889QT_END_NAMESPACE
890
891#include "qsound_pm.moc"
892
893#endif // QT_NO_SOUND
Note: See TracBrowser for help on using the repository browser.