source: trunk/src/kernel/qsound_pm.cpp@ 112

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

Kernel: QSound: Fixed sound playback on single-streamed audio devices:

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