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

Last change on this file since 46 was 46, checked in by dmik, 20 years ago

Implemented the OS/2 version of the QSound class.

File size: 21.0 KB
Line 
1/****************************************************************************
2** $Id$
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
131#define MCI_OPEN_TYPE_ID 0x00001000L
132#define MCI_OPEN_SHAREABLE 0x00002000L
133#define MCI_OPEN_MMIO 0x00004000L
134
135typedef struct _MCI_OPEN_PARMS
136{
137 HWND hwndCallback; /* PM window handle for MCI notify message */
138 USHORT usDeviceID; /* Device ID returned to user */
139 USHORT usReserved0; /* Reserved */
140 PSZ pszDeviceType; /* Device name from SYSTEM.INI */
141 PSZ pszElementName; /* Typically a file name or NULL */
142 PSZ pszAlias; /* Optional device alias */
143} MCI_OPEN_PARMS;
144typedef MCI_OPEN_PARMS *PMCI_OPEN_PARMS;
145
146typedef struct _MCI_PLAY_PARMS
147{
148 HWND hwndCallback; /* PM window handle for MCI notify message */
149 ULONG ulFrom; /* Play from this position */
150 ULONG ulTo; /* Play to this position */
151} MCI_PLAY_PARMS;
152typedef MCI_PLAY_PARMS *PMCI_PLAY_PARMS;
153
154#define MMIO_TRANSLATEDATA 0x00000001L /* Translation */
155#define MMIO_TRANSLATEHEADER 0x00000002L /* Translation */
156
157#define MMIO_READ 0x00000004L /* Open */
158
159#define MMIO_SUCCESS 0L
160
161#define MMIO_MEDIATYPE_AUDIO 0x00000002L /* Audio media */
162
163#define MCI_DEVTYPE_WAVEFORM_AUDIO 7
164
165#define MCI_NOTIFY 0x00000001L
166#define MCI_WAIT 0x00000002L
167#define MCI_FROM 0x00000004L
168
169#define MCI_OPEN 1
170#define MCI_CLOSE 2
171#define MCI_PLAY 4
172#define MCI_STOP 6
173
174#define MCI_NOTIFY_SUCCESSFUL 0x0000
175
176#define MM_MCINOTIFY 0x0500
177
178#define MCIERR_SUCCESS 0
179
180// functions resolved by mmio.dll
181
182typedef
183USHORT (APIENTRY *mmioClose_T)( HMMIO hmmio,
184 USHORT usFlags );
185typedef
186HMMIO (APIENTRY *mmioOpen_T)( PSZ pszFileName,
187 PMMIOINFO pmmioinfo,
188 ULONG ulOpenFlags );
189
190typedef
191ULONG (APIENTRY *mmioGetHeader_T)( HMMIO hmmio,
192 PVOID pHeader,
193 LONG lHeaderLength,
194 PLONG plBytesRead,
195 ULONG ulReserved,
196 ULONG ulFlags );
197
198static mmioClose_T mmioClose = 0;
199static mmioOpen_T mmioOpen = 0;
200static mmioGetHeader_T mmioGetHeader = 0;
201
202// functions resolved by mdm.dll
203
204typedef
205ULONG (APIENTRY *mciSendCommand_T)( USHORT usDeviceID,
206 USHORT usMessage,
207 ULONG ulParam1,
208 PVOID pParam2,
209 USHORT usUserParm );
210
211static mciSendCommand_T mciSendCommand = 0;
212
213////////////////////////////////////////////////////////////////////////////////
214
215class QAuBucketMMPM;
216
217class QAuServerMMPM : public QAuServer {
218
219 Q_OBJECT
220
221public:
222
223 QAuServerMMPM( QObject* parent );
224 ~QAuServerMMPM();
225
226 void init( QSound *s );
227 void play( const QString &filename );
228 void play( QSound *s );
229 void stop( QSound *s );
230 bool okay();
231
232private slots:
233
234 void aboutToQuit() { isAboutToQuit = TRUE; }
235
236private:
237
238 HWND hwnd;
239 bool isOk;
240 bool isAboutToQuit;
241
242 QLibrary mdmLib;
243 QLibrary mmioLib;
244
245 QIntDict< QAuBucketMMPM > buckets;
246
247 static const char *ClassName;
248 static MRESULT EXPENTRY WindowProc( HWND hwnd, ULONG msg,
249 MPARAM mp1, MPARAM mp2 );
250
251 friend class QAuBucketMMPM;
252};
253
254////////////////////////////////////////////////////////////////////////////////
255
256class QAuBucketMMPM : public QAuBucket {
257
258public:
259
260 QAuBucketMMPM( QAuServerMMPM *server, QSound *sound );
261 QAuBucketMMPM( QAuServerMMPM *server, const QString &soundFile );
262 ~QAuBucketMMPM();
263
264 void play();
265 void stop();
266 bool okay() { return fileHandle != NULLHANDLE && deviceId != 0; }
267
268 QSound *sound() { return snd; }
269
270 static const char *ClassName;
271
272private:
273
274 void init( const QString &fileName );
275
276 QAuServerMMPM *srv;
277 QSound *snd;
278
279#if defined(QT_DEBUG)
280 QCString fileName;
281#endif
282
283 HMMIO fileHandle;
284 USHORT deviceId;
285};
286
287////////////////////////////////////////////////////////////////////////////////
288
289QAuBucketMMPM::QAuBucketMMPM( QAuServerMMPM *server, QSound *sound ) :
290 srv( server ), snd( sound ), fileHandle( NULLHANDLE ), deviceId( 0 )
291{
292 Q_ASSERT( srv );
293 Q_ASSERT( snd );
294
295 init( snd->fileName() );
296 if ( okay() )
297 srv->buckets.insert( deviceId, this );
298}
299
300QAuBucketMMPM::QAuBucketMMPM( QAuServerMMPM *server, const QString &fileName ) :
301 srv( server ), snd( NULL ), fileHandle( NULLHANDLE ), deviceId( 0 )
302{
303 Q_ASSERT( srv );
304
305 init( fileName );
306 if ( okay() )
307 srv->buckets.insert( deviceId, this );
308}
309
310void QAuBucketMMPM::init( const QString &soundFile )
311{
312#if !defined(QT_DEBUG)
313 QCString
314#endif
315 fileName = QFile::encodeName( soundFile );
316
317 MMIOINFO mmioinfo = { 0 };
318 mmioinfo.ulTranslate = MMIO_TRANSLATEDATA | MMIO_TRANSLATEHEADER;
319 fileHandle = mmioOpen( fileName.data(), &mmioinfo, MMIO_READ );
320 if ( fileHandle == NULLHANDLE ) {
321#if defined(QT_DEBUG)
322 qDebug( "QAuBucketMMPM: falied to open sound file [%s]",
323 fileName.data() );
324#endif
325 return;
326 }
327
328 MMAUDIOHEADER mmah;
329 LONG bytesRead = 0;
330 ULONG rc = mmioGetHeader( fileHandle, &mmah, sizeof(mmah), &bytesRead, 0, 0 );
331 if ( rc != MMIO_SUCCESS || mmah.ulMediaType != MMIO_MEDIATYPE_AUDIO ) {
332#if defined(QT_DEBUG)
333 qDebug( "QAuBucketMMPM: [%s] is not a sound file or "
334 "has an unsupported format (rc=%04ld)", fileName.data(), rc );
335#endif
336 return;
337 }
338
339 MCI_OPEN_PARMS openParams = { 0 };
340 openParams.pszDeviceType = (PSZ) MAKEULONG( MCI_DEVTYPE_WAVEFORM_AUDIO, 0 );
341 openParams.pszElementName = (PSZ) fileHandle;
342 rc = mciSendCommand( 0, MCI_OPEN, MCI_WAIT | MCI_OPEN_MMIO |
343 MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
344 &openParams, 0 );
345 if ( rc != MCIERR_SUCCESS ) {
346#if defined(QT_DEBUG)
347 qDebug( "QAuBucketMMPM: failed to open a device for sound file [%s] "
348 "(rc=%04ld)", fileName.data(), rc );
349#endif
350 return;
351 }
352
353 deviceId = openParams.usDeviceID;
354}
355
356QAuBucketMMPM::~QAuBucketMMPM()
357{
358#if defined(QT_QSOUND_DEBUG)
359 qDebug( "~QAuBucketMMPM(): this=%p", this );
360#endif
361
362 if ( okay() )
363 srv->buckets.remove( deviceId );
364
365 if ( okay() && snd && !srv->isAboutToQuit ) {
366 // in order to guarantee that the device is closed only after all
367 // messages for the particular device ID are delivered to WindowProc()
368 // and that no other device is assigned the same ID until then, we post
369 // a special message containing fileHandle and deviceId to be closed
370 WinPostMsg( srv->hwnd, WM_USER,
371 MPFROMLONG( fileHandle ), MPFROMSHORT( deviceId ) );
372 } else {
373 if ( deviceId != 0 )
374 mciSendCommand( deviceId, MCI_CLOSE, MCI_WAIT, NULL, 0 );
375 if ( fileHandle != NULLHANDLE )
376 mmioClose( fileHandle, 0 );
377 }
378}
379
380void QAuBucketMMPM::play()
381{
382 MCI_PLAY_PARMS playParams = { 0 };
383 playParams.hwndCallback = srv->hwnd;
384 playParams.ulFrom = 0; // always play from the beginning
385
386 ULONG rc = mciSendCommand( deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM,
387 &playParams, 0 );
388
389 if ( rc != MCIERR_SUCCESS ) {
390#if defined(QT_DEBUG)
391 qDebug( "QAuBucketMMPM: failed to play sound file [%s] (rc=%04ld)",
392 fileName.data(), rc );
393#endif
394 }
395}
396
397void QAuBucketMMPM::stop()
398{
399 ULONG rc = mciSendCommand( deviceId, MCI_STOP, MCI_WAIT, NULL, 0 );
400
401 if ( rc != MCIERR_SUCCESS ) {
402#if defined(QT_DEBUG)
403 qDebug( "QAuBucketMMPM: failed to stop sound file [%s] (rc=%04ld)",
404 fileName.data(), rc );
405#endif
406 }
407}
408
409////////////////////////////////////////////////////////////////////////////////
410
411const char *QAuServerMMPM::ClassName = "QAuServerMMPM";
412
413QAuServerMMPM::QAuServerMMPM( QObject* parent ) :
414 QAuServer( parent, "OS/2 MMPM Audio Server" ),
415 isOk( FALSE ), isAboutToQuit( FALSE ),
416 mdmLib( "mdm.dll" ), mmioLib( "mmio.dll" )
417{
418 WinRegisterClass( 0, ClassName, WindowProc, 0, sizeof(PVOID) );
419
420 hwnd = WinCreateWindow( HWND_OBJECT, ClassName, NULL, 0, 0, 0, 0, 0,
421 NULL, HWND_BOTTOM, 0,
422 this, NULL );
423
424 if ( hwnd == NULLHANDLE ) {
425#if defined(QT_DEBUG)
426 qSystemWarning( "QAuServerMMPM: falied to create an object window" );
427#endif
428 return;
429 }
430
431 // resolve functions
432 mmioClose = (mmioClose_T) mmioLib.resolve( "mmioClose" );
433 mmioOpen = (mmioOpen_T) mmioLib.resolve( "mmioOpen" );
434 mmioGetHeader = (mmioGetHeader_T) mmioLib.resolve( "mmioGetHeader" );
435 mciSendCommand = (mciSendCommand_T) mdmLib.resolve( "mciSendCommand" );
436 if ( !mmioClose || !mmioGetHeader || !mmioOpen || !mciSendCommand ) {
437#if defined(QT_DEBUG)
438 qDebug( "QAuServerMMPM: failed to resolve MMPM system functions" );
439#endif
440 return;
441 }
442
443 // try to open the default waveaudio device to ensure it exists
444 MCI_OPEN_PARMS openParams = { 0 };
445 openParams.pszDeviceType = (PSZ) MAKEULONG( MCI_DEVTYPE_WAVEFORM_AUDIO, 0 );
446 ULONG rc = mciSendCommand( 0, MCI_OPEN, MCI_WAIT |
447 MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
448 &openParams, 0 );
449 if ( rc != MCIERR_SUCCESS ) {
450#if defined(QT_DEBUG)
451 qDebug( "QAuServerMMPM: failed to open the default waveaudio device "
452 "(rc=%04ld)", rc );
453#endif
454 return;
455 }
456
457 // close the device opened above
458 mciSendCommand( openParams.usDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0 );
459
460 connect( qApp, SIGNAL( aboutToQuit() ), SLOT( aboutToQuit() ) );
461
462 isOk = TRUE;
463}
464
465QAuServerMMPM::~QAuServerMMPM()
466{
467#if defined(QT_QSOUND_DEBUG)
468 qDebug( "~QAuServerMMPM(): buckets left: %d", buckets.count() );
469#endif
470
471 // deassociate all remaining buckets from QSound objects and delete them
472 // (note that deleting a bucket will remove it from the dictionary, which
473 // in turn will update all dictionary iterators to point to the next item,
474 // so there is no iterator increment statement below)
475 QIntDictIterator< QAuBucketMMPM > it( buckets );
476 while ( it.current() ) {
477 QSound *s = it.current()->sound();
478 if ( s ) {
479 // the below call will delete the associated bucket
480 setBucket ( s, 0 );
481 } else {
482 delete it.current();
483 }
484 }
485
486 Q_ASSERT( buckets.count() == 0 );
487
488 mmioClose = 0;
489 mmioOpen = 0;
490 mmioGetHeader = 0;
491 mciSendCommand = 0;
492}
493
494void QAuServerMMPM::init( QSound *s )
495{
496 Q_ASSERT( s );
497
498 if ( !okay() )
499 return;
500
501 QAuBucketMMPM *b = new QAuBucketMMPM( this, s );
502 if ( b->okay() ) {
503 setBucket( s, b );
504#if defined(QT_QSOUND_DEBUG)
505 qDebug( "init(): bucket=%p sound=[%s]", b, s->fileName().latin1() );
506#endif
507 } else {
508 setBucket( s, 0 );
509 // b is deleted in setBucket()
510 }
511}
512
513void QAuServerMMPM::play( const QString &filename )
514{
515 if ( !okay() )
516 return;
517
518 QAuBucketMMPM *b = new QAuBucketMMPM( this, filename );
519 if ( b->okay() ) {
520#if defined(QT_QSOUND_DEBUG)
521 qDebug( "play(QString): bucket=%p sound=[%s]", b, filename.latin1() );
522#endif
523 b->play();
524 // b will be deleted in WindowProc()
525 } else {
526 delete b;
527 }
528}
529
530void QAuServerMMPM::play( QSound *s )
531{
532 Q_ASSERT( s );
533
534 if ( !okay() || !isRelevant( s ) ) {
535 // no MMPM is available or a wrong sound, just decrease loops to zero
536 while ( decLoop( s ) > 0 ) ;
537 return;
538 }
539
540 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >( bucket( s ) );
541 if ( b ) {
542#if defined(QT_QSOUND_DEBUG)
543 qDebug( "play(QSound): bucket=%p sound=[%s]", b, s->fileName().latin1() );
544#endif
545 b->play();
546 } else {
547 // failed to create a bucket in init(), just decrease loops to zero
548 while ( decLoop( s ) > 0 ) ;
549 }
550}
551
552void QAuServerMMPM::stop( QSound *s )
553{
554 Q_ASSERT( s );
555
556 if ( !okay() || !isRelevant( s ) )
557 return;
558
559 QAuBucketMMPM *b = static_cast< QAuBucketMMPM * >( bucket( s ) );
560 if ( b )
561 b->stop();
562}
563
564bool QAuServerMMPM::okay()
565{
566 return isOk;
567}
568
569MRESULT EXPENTRY QAuServerMMPM::WindowProc( HWND hwnd, ULONG msg,
570 MPARAM mp1, MPARAM mp2 )
571{
572 QAuServerMMPM * that =
573 static_cast< QAuServerMMPM * >( WinQueryWindowPtr( hwnd, 0 ) );
574
575 switch ( msg ) {
576 case WM_CREATE: {
577 QAuServerMMPM * that = static_cast< QAuServerMMPM * >( mp1 );
578 if ( !that )
579 return (MRESULT) TRUE;
580 WinSetWindowPtr( hwnd, 0, that );
581 return (MRESULT) FALSE;
582 }
583 case WM_DESTROY: {
584 return 0;
585 }
586 case MM_MCINOTIFY: {
587 if ( !that )
588 return 0;
589
590 USHORT code = SHORT1FROMMP( mp1 );
591 USHORT deviceId = SHORT1FROMMP( mp2 );
592#if defined(QT_QSOUND_DEBUG)
593 qDebug( "MM_MCINOTIFY: code=%04hX deviceId=%04hX", code, deviceId );
594#endif
595
596 QAuBucketMMPM *b = that->buckets.find( deviceId );
597 if ( !b )
598 return 0;
599#if defined(QT_QSOUND_DEBUG)
600 qDebug( "MM_MCINOTIFY: bucket=%p", b );
601#endif
602
603 QSound *sound = b->sound();
604 if ( sound ) {
605 if ( code == MCI_NOTIFY_SUCCESSFUL ) {
606 // play the sound until there are no loops left
607 int loopsLeft = that->decLoop( sound );
608 if ( loopsLeft != 0 )
609 b->play();
610 }
611 } else {
612 // delete QSound'less bucket when finished or stopped playing
613 delete b;
614 }
615
616 return 0;
617 }
618 case WM_USER: {
619 HMMIO fileHandle = LONGFROMMP( mp1 );
620 USHORT deviceId = SHORT1FROMMP( mp2 );
621#if defined(QT_QSOUND_DEBUG)
622 qDebug( "WM_USER: deviceId=%04hX", deviceId );
623#endif
624 // there should be no buckets with the given ID at this point
625 Q_ASSERT( that->buckets.find( deviceId ) == 0 );
626 mciSendCommand( deviceId, MCI_CLOSE, MCI_WAIT, NULL, 0 );
627 mmioClose( fileHandle, 0 );
628 }
629 }
630
631 return 0;
632}
633
634QAuServer *qt_new_audio_server()
635{
636 return new QAuServerMMPM( qApp );
637}
638
639#include "qsound_pm.moc"
640
641#endif // QT_NO_SOUND
Note: See TracBrowser for help on using the repository browser.