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

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

Kernel: QSound: Commented out the debug macro mistakenly left after r120.

  • Property svn:keywords set to Id
File size: 28.5 KB
Line 
1/****************************************************************************
2** $Id: qsound_pm.cpp 122 2006-09-03 20:51:05Z 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#include <stdlib.h> // for getenv()
52
53//#define QT_QSOUND_DEBUG
54
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
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
176#define MCI_OPEN 1
177#define MCI_CLOSE 2
178#define MCI_PLAY 4
179#define MCI_STOP 6
180#define MCI_ACQUIREDEVICE 23
181#define MCI_RELEASEDEVICE 24
182
183#define MM_MCINOTIFY 0x0500
184#define MM_MCIPASSDEVICE 0x0501
185
186#define MCI_NOTIFY_SUCCESSFUL 0x0000
187#define MCI_NOTIFY_SUPERSEDED 0x0001
188
189#define MCI_LOSING_USE 0x00000001L
190#define MCI_GAINING_USE 0x00000002L
191
192#define MCIERR_BASE 5000
193#define MCIERR_SUCCESS 0
194#define MCIERR_INSTANCE_INACTIVE (MCIERR_BASE + 34)
195#define MCIERR_DEVICE_LOCKED (MCIERR_BASE + 32)
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
251 void serverUninit();
252
253private:
254
255 HWND hwnd;
256 bool isOk;
257
258 QLibrary mdmLib;
259 QLibrary mmioLib;
260
261 QIntDict< QAuBucketMMPM > bucketMap;
262 QPtrList< QAuBucketMMPM > bucketList;
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
277 enum State { Stopped, Playing, Waiting };
278
279 QAuBucketMMPM( QAuServerMMPM *server, QSound *sound );
280 QAuBucketMMPM( QAuServerMMPM *server, const QString &soundFile );
281 ~QAuBucketMMPM();
282
283 void open();
284 void close( bool atExit = FALSE );
285 bool play();
286 void stop();
287 bool okay() { return fileHandle != NULLHANDLE; }
288
289 QSound *sound() { return snd; }
290 State state() { return st; }
291
292 void onDeviceGained( bool gained );
293
294#if defined(QT_DEBUG)
295 QCString fileName() { return fName; };
296#endif
297
298private:
299
300 void init( const QString &fileName );
301
302 QAuServerMMPM *srv;
303 QSound *snd;
304
305#if defined(QT_DEBUG)
306 QCString fName;
307#endif
308
309 HMMIO fileHandle;
310 USHORT deviceId;
311
312 State st;
313};
314
315////////////////////////////////////////////////////////////////////////////////
316
317QAuBucketMMPM::QAuBucketMMPM( QAuServerMMPM *server, QSound *sound ) :
318 srv( server ), snd( sound ), fileHandle( NULLHANDLE ), deviceId( 0 ),
319 st( Stopped )
320{
321 Q_ASSERT( srv );
322 Q_ASSERT( snd );
323 if ( !srv || !snd )
324 return;
325
326 init( snd->fileName() );
327}
328
329QAuBucketMMPM::QAuBucketMMPM( QAuServerMMPM *server, const QString &fileName ) :
330 srv( server ), snd( NULL ), fileHandle( NULLHANDLE ), deviceId( 0 ),
331 st( Stopped )
332{
333 Q_ASSERT( srv );
334 if ( !srv )
335 return;
336
337 init( fileName );
338}
339
340void QAuBucketMMPM::init( const QString &soundFile )
341{
342 Q_ASSERT( fileHandle == NULLHANDLE );
343 if ( fileHandle != NULLHANDLE )
344 return;
345
346#if !defined(QT_DEBUG)
347 QCString
348#endif
349 fName = QFile::encodeName( soundFile );
350
351 MMIOINFO mmioinfo = { 0 };
352 mmioinfo.ulTranslate = MMIO_TRANSLATEDATA | MMIO_TRANSLATEHEADER;
353 fileHandle = mmioOpen( fName.data(), &mmioinfo, MMIO_READ );
354 if ( fileHandle == NULLHANDLE ) {
355#if defined(QT_DEBUG)
356 qDebug( "QAuBucketMMPM: falied to open sound file [%s]", fName.data() );
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 "
367 "has an unsupported format (rc=%04hu:%04hu)",
368 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) );
369#endif
370 mmioClose( fileHandle, 0 );
371 fileHandle = NULLHANDLE;
372 return;
373 }
374
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
403 MCI_OPEN_PARMS openParams = { 0 };
404 openParams.hwndCallback = srv->hwnd;
405 openParams.pszDeviceType = (PSZ) MAKEULONG( MCI_DEVTYPE_WAVEFORM_AUDIO, 0 );
406 openParams.pszElementName = (PSZ) fileHandle;
407 ULONG params = MCI_WAIT | MCI_OPEN_MMIO | MCI_READONLY | MCI_OPEN_TYPE_ID;
408 if ( getenv( "QT_PM_NO_SOUND_SHARE" ) != NULL ) {
409#if defined(QT_DEBUG)
410 qDebug( "QAuBucketMMPM: WARNING: opening device for sound file [%s] in "
411 "exclusive mode due to QT_PM_NO_SOUND_SHARE=%s",
412 fName.data(), getenv( "QT_PM_NO_SOUND_SHARE" ) );
413#endif
414 } else {
415 params |= MCI_OPEN_SHAREABLE;
416 }
417 ULONG rc = mciSendCommand( 0, MCI_OPEN, params, &openParams, 0 );
418 if ( rc != MCIERR_SUCCESS ) {
419#if defined(QT_DEBUG)
420 qDebug( "QAuBucketMMPM: failed to open a device for sound file [%s] "
421 "(rc=%04hu:%04hu)", fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) );
422#endif
423 return;
424 }
425
426 deviceId = openParams.usDeviceID;
427
428 srv->bucketMap.insert( deviceId, this );
429
430#if defined(QT_QSOUND_DEBUG)
431 qDebug( "QAuBucketMMPM::open(): {%p} [%s] deviceId=%08hu",
432 this, fName.data(), deviceId );
433#endif
434}
435
436void QAuBucketMMPM::close( bool atExit /* = FALSE */)
437{
438 Q_ASSERT( deviceId );
439 if ( !deviceId )
440 return;
441
442#if defined(QT_QSOUND_DEBUG)
443 qDebug( "QAuBucketMMPM()::close(): {%p} [%s]", this, fName.data() );
444#endif
445
446 // remove from the map anyway -- we don't plan to retry
447 srv->bucketMap.remove( deviceId );
448
449 // Use MCI_CLOSE_EXIT to tell the media control driver it should
450 // close immediately, w/o doing for any notifications, etc.
451 ULONG param = atExit ? MCI_CLOSE_EXIT : 0;
452
453 ULONG rc = mciSendCommand( deviceId, MCI_CLOSE, param, NULL, 0 );
454#if defined(QT_DEBUG)
455 if ( rc != MCIERR_SUCCESS )
456 qDebug( "QAuBucketMMPM: failed to close the device for sound file [%s] "
457 "(rc=%04hu:%04hu)",
458 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) );
459#endif
460 Q_UNUSED( rc );
461
462 st = Stopped;
463 deviceId = 0;
464}
465
466bool QAuBucketMMPM::play()
467{
468#if defined(QT_QSOUND_DEBUG)
469 qDebug( "QAuBucketMMPM()::play(): {%p} [%s]", this, fName.data() );
470#endif
471
472 if ( !deviceId ) {
473 open();
474 if ( !deviceId )
475 return FALSE;
476 }
477
478 MCI_PLAY_PARMS playParams = { 0 };
479 playParams.hwndCallback = srv->hwnd;
480 playParams.ulFrom = 0; // always play from the beginning
481
482 ULONG rc = mciSendCommand( deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
483 &playParams, 0 );
484
485 if ( LOUSHORT( rc ) == MCIERR_INSTANCE_INACTIVE ) {
486 // There are not enough simultaneous audio streams. Try to acquire the
487 // resources and play again. Note that if the device is already acquired
488 // for exclusive use, this command will not wait but return immediately.
489 rc = mciSendCommand( deviceId, MCI_ACQUIREDEVICE, MCI_WAIT, NULL, 0 );
490 if ( rc == MCIERR_SUCCESS ) {
491 rc = mciSendCommand( deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
492 &playParams, 0 );
493 } else if ( LOUSHORT( rc ) == MCIERR_DEVICE_LOCKED &&
494 snd && snd->loops() < 0 ) {
495 // Enter the special state to let this infinitive sound start
496 // playing when the resource becomes available.
497 st = Waiting;
498 return FALSE;
499 }
500 }
501
502 if ( rc == MCIERR_SUCCESS ) {
503 st = Playing;
504 } else {
505 st = Stopped;
506#if defined(QT_DEBUG)
507 qDebug( "QAuBucketMMPM: failed to play sound file [%s] (rc=%04hu:%04hu)",
508 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) );
509#endif
510 }
511
512 return st == Playing;
513}
514
515void QAuBucketMMPM::stop()
516{
517 if ( st == Stopped )
518 return;
519
520 // always go to Stopped -- we won't retry
521 // (this is also used in MM_MCINOTIFY processing)
522 st = Stopped;
523
524 ULONG rc = mciSendCommand( deviceId, MCI_STOP, MCI_WAIT, NULL, 0 );
525
526 if ( rc == MCIERR_SUCCESS ) {
527 // nothing
528 } else if ( LOUSHORT( rc ) == MCIERR_INSTANCE_INACTIVE ) {
529 // The infinite QSound-full bucket is now suspended (some other instance
530 // has gained the resource). Close this instance to prevent it from
531 // being automatically resumed later.
532 close();
533 } else {
534#if defined(QT_DEBUG)
535 qDebug( "QAuBucketMMPM: failed to stop sound file [%s] (rc=%04hu:%04hu)",
536 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) );
537#endif
538 // last chance to stop
539 close();
540 }
541}
542
543void QAuBucketMMPM::onDeviceGained( bool gained )
544{
545 if ( gained ) {
546 // We gained the device resource.
547 if ( st == Waiting ) {
548 /// @todo (dmik) For some reason, starting playback from here
549 // (i.e. when we've been given the deivce resource back after
550 // some other application finished using it exclusively), we can
551 // get error 5053 (MCIERR_NO_CONNECTION) or even undocumented 5659.
552 // When it happens, subsequent attempts to play something will
553 // result into an unkillable hang... Experimemtal for now.
554#if 1
555 // A QSound-full bucket attempted to play eventually regained
556 // the resource. Start playing.
557 play();
558#else
559 st = Stopped;
560#endif
561 }
562 } else {
563 // We lost the device resource.
564 if ( st == Playing ) {
565 // Close the instance to prevent the playback from being resumed
566 // when the device is auto-gained again (by another instance that
567 // uses MCI_RETURN_RESOURCE in MCI_RELEASEDEVICE). The exception is
568 // a QSound-full bucket with an infinitive loop (that will continue
569 // playing when the resuorce is back).
570 if ( snd ) {
571 // infinitive loop?
572 if ( snd->loops() < 0 )
573 return;
574 // decrease loops to zero
575 while ( srv->decLoop( snd ) > 0 ) ;
576 }
577 close();
578 if ( !snd ) {
579 // delete QSound-less bucket
580 delete this;
581 }
582 }
583 }
584}
585
586////////////////////////////////////////////////////////////////////////////////
587
588const char *QAuServerMMPM::ClassName = "QAuServerMMPM";
589
590QAuServerMMPM::QAuServerMMPM( QObject* parent ) :
591 QAuServer( parent, "OS/2 MMPM Audio Server" ),
592 hwnd( NULLHANDLE ), isOk( FALSE ),
593 mdmLib( "mdm.dll" ), mmioLib( "mmio.dll" )
594{
595 WinRegisterClass( 0, ClassName, WindowProc, 0, sizeof(PVOID) );
596
597 hwnd = WinCreateWindow( HWND_OBJECT, ClassName, NULL, 0, 0, 0, 0, 0,
598 NULL, HWND_BOTTOM, 0,
599 this, NULL );
600
601 if ( hwnd == NULLHANDLE ) {
602#if defined(QT_DEBUG)
603 qSystemWarning( "QAuServerMMPM: falied to create an object window" );
604#endif
605 return;
606 }
607
608 // resolve functions
609 mmioClose = (mmioClose_T) mmioLib.resolve( "mmioClose" );
610 mmioOpen = (mmioOpen_T) mmioLib.resolve( "mmioOpen" );
611 mmioGetHeader = (mmioGetHeader_T) mmioLib.resolve( "mmioGetHeader" );
612 mciSendCommand = (mciSendCommand_T) mdmLib.resolve( "mciSendCommand" );
613 if ( !mmioClose || !mmioGetHeader || !mmioOpen || !mciSendCommand ) {
614#if defined(QT_DEBUG)
615 qDebug( "QAuServerMMPM: failed to resolve MMPM system functions" );
616#endif
617 return;
618 }
619
620 // try to open the default waveaudio device to ensure it exists
621 MCI_OPEN_PARMS openParams = { 0 };
622 openParams.pszDeviceType = (PSZ) MAKEULONG( MCI_DEVTYPE_WAVEFORM_AUDIO, 0 );
623 ULONG rc = mciSendCommand( 0, MCI_OPEN, MCI_WAIT |
624 MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
625 &openParams, 0 );
626 if ( rc != MCIERR_SUCCESS ) {
627#if defined(QT_DEBUG)
628 qDebug( "QAuServerMMPM: failed to open the default waveaudio device "
629 "(rc=%04hu:%04hu)", HIUSHORT( rc ), LOUSHORT( rc ) );
630#endif
631 return;
632 }
633
634 // close the device opened above
635 mciSendCommand( openParams.usDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0 );
636
637 connect( qApp, SIGNAL( aboutToQuit() ), SLOT( serverUninit() ) );
638
639 isOk = TRUE;
640}
641
642QAuServerMMPM::~QAuServerMMPM()
643{
644 // still alive?
645 if ( hwnd )
646 serverUninit();
647}
648
649void QAuServerMMPM::serverUninit()
650{
651#if defined(QT_QSOUND_DEBUG)
652 qDebug( "QAuServerMMPM::serverUninit(): buckets left: %d",
653 bucketList.count() );
654#endif
655
656 Q_ASSERT( hwnd );
657 if ( hwnd == NULLHANDLE )
658 return;
659
660 WinDestroyWindow( hwnd );
661 hwnd = NULLHANDLE;
662
663 // Deassociate all remaining buckets from QSound objects and delete them
664 // (note that deleting a bucket will remove it from the list, which
665 // in turn will advance the current item to the next one, so current() is
666 // is used instead of next() in the loop).
667 for ( QAuBucketMMPM *b = bucketList.first(); b; b = bucketList.current() ) {
668 QSound *s = b->sound();
669 if ( s ) {
670 // the below call will delete the associated bucket
671 setBucket ( s, 0 );
672 } else {
673 delete b;
674 }
675 }
676
677 Q_ASSERT( bucketList.count() == 0 );
678 Q_ASSERT( bucketMap.count() == 0 );
679
680 mmioClose = 0;
681 mmioOpen = 0;
682 mmioGetHeader = 0;
683 mciSendCommand = 0;
684}
685
686void QAuServerMMPM::init( QSound *s )
687{
688 Q_ASSERT( s );
689
690 if ( !okay() )
691 return;
692
693 QAuBucketMMPM *b = new QAuBucketMMPM( this, s );
694 if ( b->okay() ) {
695 setBucket( s, b );
696#if defined(QT_QSOUND_DEBUG)
697 qDebug( "QAuServerMMPM::init(): bucket=%p [%s]",
698 b, b->fileName().data() );
699#endif
700 } else {
701 setBucket( s, 0 );
702 // b is deleted in setBucket()
703 }
704}
705
706void QAuServerMMPM::play( const QString &filename )
707{
708 if ( !okay() )
709 return;
710
711 QAuBucketMMPM *b = new QAuBucketMMPM( this, filename );
712 if ( b->okay() ) {
713#if defined(QT_QSOUND_DEBUG)
714 qDebug( "play(QString): bucket=%p [%s]", b, b->fileName().data() );
715#endif
716 if ( b->play() ) {
717 // b will be deleted in WindowProc() or in onDeviceGained()
718 } else {
719 delete b;
720 }
721 } else {
722 delete b;
723 }
724}
725
726void QAuServerMMPM::play( QSound *s )
727{
728 Q_ASSERT( s );
729
730 if ( !okay() || !isRelevant( s ) ) {
731 // no MMPM is available or a wrong sound, just decrease loops to zero
732 while ( decLoop( s ) > 0 ) ;
733 return;
734 }
735
736 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >( bucket( s ) );
737 if ( b ) {
738#if defined(QT_QSOUND_DEBUG)
739 qDebug( "play(QSound): bucket=%p [%s]", b, b->fileName().data() );
740#endif
741 b->play();
742 } else {
743 // failed to create a bucket in init(), just decrease loops to zero
744 while ( decLoop( s ) > 0 ) ;
745 }
746}
747
748void QAuServerMMPM::stop( QSound *s )
749{
750 Q_ASSERT( s );
751
752 if ( !okay() || !isRelevant( s ) )
753 return;
754
755 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >( bucket( s ) );
756 if ( b )
757 b->stop();
758}
759
760bool QAuServerMMPM::okay()
761{
762 return isOk;
763}
764
765MRESULT EXPENTRY QAuServerMMPM::WindowProc( HWND hwnd, ULONG msg,
766 MPARAM mp1, MPARAM mp2 )
767{
768 QAuServerMMPM * that =
769 static_cast< QAuServerMMPM * >( WinQueryWindowPtr( hwnd, 0 ) );
770
771 switch ( msg ) {
772 case WM_CREATE: {
773 QAuServerMMPM * that = static_cast< QAuServerMMPM * >( mp1 );
774 if ( !that )
775 return (MRESULT) TRUE;
776 WinSetWindowPtr( hwnd, 0, that );
777 return (MRESULT) FALSE;
778 }
779 case WM_DESTROY: {
780 return 0;
781 }
782 case MM_MCINOTIFY: {
783 if ( !that )
784 return 0;
785
786 USHORT code = SHORT1FROMMP( mp1 );
787 USHORT deviceId = SHORT1FROMMP( mp2 );
788 USHORT mcimsg = SHORT2FROMMP( mp2 );
789#if defined(QT_QSOUND_DEBUG)
790 qDebug( "MM_MCINOTIFY: code=0x%04hX, deviceId=%04hu, mcimsg=%04hu",
791 code, deviceId, mcimsg );
792#endif
793 Q_ASSERT( mcimsg == MCI_PLAY );
794 if ( mcimsg != MCI_PLAY )
795 return 0;
796
797 QAuBucketMMPM *b = that->bucketMap.find( deviceId );
798 // There will be a late notification if the sound is
799 // playing when close() happens. Just return silently.
800 if ( !b )
801 return 0;
802
803#if defined(QT_QSOUND_DEBUG)
804 qDebug( "MM_MCINOTIFY: bucket=%p [%s]", b, b->fileName().data() );
805#endif
806
807 QSound *sound = b->sound();
808 if ( sound ) {
809 bool returnResource = FALSE;
810 if ( b->state() == QAuBucketMMPM::Stopped ) {
811 // It's possible that MCI_STOP is issued right after MCI_PLAY
812 // has successfilly finished and sent MM_MCINOTIFY but before
813 // we start it again here. Obey the STOP request.
814 returnResource = TRUE;
815 } else if ( code == MCI_NOTIFY_SUCCESSFUL ) {
816 // play the sound until there are no loops left
817 int loopsLeft = that->decLoop( sound );
818 if ( loopsLeft != 0 )
819 b->play();
820 else
821 returnResource = TRUE;
822 } else if ( code != MCI_NOTIFY_SUPERSEDED ) {
823 returnResource = TRUE;
824 }
825 if ( returnResource ) {
826 // let infinitive sounds continue playing
827 mciSendCommand( deviceId, MCI_RELEASEDEVICE,
828 MCI_RETURN_RESOURCE, NULL, 0 );
829 }
830 } else {
831 // delete QSound-less bucket when finished or stopped playing
832 // (closing the instance will return the resource)
833 delete b;
834 }
835
836 return 0;
837 }
838 case MM_MCIPASSDEVICE: {
839 if ( !that )
840 return 0;
841
842 USHORT deviceId = SHORT1FROMMP( mp1 );
843 USHORT event = SHORT1FROMMP( mp2 );
844#if defined(QT_QSOUND_DEBUG)
845 qDebug( "MM_MCIPASSDEVICE: deviceId=%04hu, event=0x%04hX",
846 deviceId, event );
847#endif
848 QAuBucketMMPM *b = that->bucketMap.find( deviceId );
849 if ( !b )
850 return 0;
851
852#if defined(QT_QSOUND_DEBUG)
853 qDebug( "MM_MCIPASSDEVICE: bucket=%p [%s]", b, b->fileName().data() );
854#endif
855 // Note: this call may delete b
856 b->onDeviceGained( event == MCI_GAINING_USE );
857
858 return 0;
859 }
860 }
861
862 return 0;
863}
864
865QAuServer *qt_new_audio_server()
866{
867 return new QAuServerMMPM( qApp );
868}
869
870#include "qsound_pm.moc"
871
872#endif // QT_NO_SOUND
Note: See TracBrowser for help on using the repository browser.