source: trunk/src/kernel/qsound_pm.cpp

Last change on this file was 144, checked in by dmik, 19 years ago

Kernel: QSound: Due to problems with the most commonly used uniaud driver when it comes to play too many (>10) sound files simultaneously in shareable mode sound files are now opened in exclusive mode by default. It is necessary to set the QT_PM_SOUND_SHARE environment variable in order to switch back to shareable mode (QT_PM_NO_SOUND_SHARE is still there and has precedence).

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