Changeset 250 for trunk/src/gui/kernel/qsound_pm.cpp
- Timestamp:
- Oct 19, 2009, 9:12:20 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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.