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