Changeset 112
- Timestamp:
- Aug 3, 2006, 5:22:52 PM (19 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kernel/qsound_pm.cpp
r47 r112 129 129 typedef MMAUDIOHEADER *PMMAUDIOHEADER; 130 130 131 #define MCI_OPEN_TYPE_ID 0x00001000L132 #define MCI_OPEN_SHAREABLE 0x00002000L133 #define MCI_OPEN_MMIO 0x00004000L134 135 131 typedef struct _MCI_OPEN_PARMS 136 132 { … … 167 163 #define MCI_FROM 0x00000004L 168 164 165 #define MCI_OPEN_TYPE_ID 0x00001000L 166 #define MCI_OPEN_SHAREABLE 0x00002000L 167 #define MCI_OPEN_MMIO 0x00004000L 168 #define MCI_READONLY 0x00008000L 169 170 #define MCI_RETURN_RESOURCE 0x00000100L 171 172 #define MCI_CLOSE_EXIT 0x10000000L 173 169 174 #define MCI_OPEN 1 170 175 #define MCI_CLOSE 2 171 176 #define MCI_PLAY 4 172 177 #define MCI_STOP 6 178 #define MCI_ACQUIREDEVICE 23 179 #define MCI_RELEASEDEVICE 24 180 181 #define MM_MCINOTIFY 0x0500 182 #define MM_MCIPASSDEVICE 0x0501 173 183 174 184 #define MCI_NOTIFY_SUCCESSFUL 0x0000 175 176 #define MM_MCINOTIFY 0x0500 177 185 #define MCI_NOTIFY_SUPERSEDED 0x0001 186 187 #define MCI_LOSING_USE 0x00000001L 188 #define MCI_GAINING_USE 0x00000002L 189 190 #define MCIERR_BASE 5000 178 191 #define MCIERR_SUCCESS 0 192 #define MCIERR_INSTANCE_INACTIVE (MCIERR_BASE + 34) 193 #define MCIERR_DEVICE_LOCKED (MCIERR_BASE + 32) 179 194 180 195 // functions resolved by mmio.dll … … 232 247 private slots: 233 248 234 void aboutToQuit() { isAboutToQuit = TRUE; }249 void serverUninit(); 235 250 236 251 private: … … 238 253 HWND hwnd; 239 254 bool isOk; 240 bool isAboutToQuit;241 255 242 256 QLibrary mdmLib; 243 257 QLibrary mmioLib; 244 258 245 QIntDict< QAuBucketMMPM > buckets; 259 QIntDict< QAuBucketMMPM > bucketMap; 260 QPtrList< QAuBucketMMPM > bucketList; 246 261 247 262 static const char *ClassName; … … 258 273 public: 259 274 275 enum State { Stopped, Playing, Waiting }; 276 260 277 QAuBucketMMPM( QAuServerMMPM *server, QSound *sound ); 261 278 QAuBucketMMPM( QAuServerMMPM *server, const QString &soundFile ); 262 279 ~QAuBucketMMPM(); 263 280 264 void play(); 281 void open(); 282 void close( bool atExit = FALSE ); 283 bool play(); 265 284 void stop(); 266 bool okay() { return fileHandle != NULLHANDLE && deviceId != 0; }285 bool okay() { return fileHandle != NULLHANDLE; } 267 286 268 287 QSound *sound() { return snd; } 269 270 static const char *ClassName; 288 State state() { return st; } 289 290 void onDeviceGained( bool gained ); 291 292 #if defined(QT_DEBUG) 293 QCString fileName() { return fName; }; 294 #endif 271 295 272 296 private: … … 278 302 279 303 #if defined(QT_DEBUG) 280 QCString f ileName;304 QCString fName; 281 305 #endif 282 306 283 307 HMMIO fileHandle; 284 308 USHORT deviceId; 309 310 State st; 285 311 }; 286 312 … … 288 314 289 315 QAuBucketMMPM::QAuBucketMMPM( QAuServerMMPM *server, QSound *sound ) : 290 srv( server ), snd( sound ), fileHandle( NULLHANDLE ), deviceId( 0 ) 316 srv( server ), snd( sound ), fileHandle( NULLHANDLE ), deviceId( 0 ), 317 st( Stopped ) 291 318 { 292 319 Q_ASSERT( srv ); 293 320 Q_ASSERT( snd ); 321 if ( !srv || !snd ) 322 return; 294 323 295 324 init( snd->fileName() ); 296 if ( okay() )297 srv->buckets.insert( deviceId, this );298 325 } 299 326 300 327 QAuBucketMMPM::QAuBucketMMPM( QAuServerMMPM *server, const QString &fileName ) : 301 srv( server ), snd( NULL ), fileHandle( NULLHANDLE ), deviceId( 0 ) 328 srv( server ), snd( NULL ), fileHandle( NULLHANDLE ), deviceId( 0 ), 329 st( Stopped ) 302 330 { 303 331 Q_ASSERT( srv ); 332 if ( !srv ) 333 return; 304 334 305 335 init( fileName ); 306 if ( okay() )307 srv->buckets.insert( deviceId, this );308 336 } 309 337 310 338 void QAuBucketMMPM::init( const QString &soundFile ) 311 339 { 340 Q_ASSERT( fileHandle == NULLHANDLE ); 341 if ( fileHandle != NULLHANDLE ) 342 return; 343 312 344 #if !defined(QT_DEBUG) 313 345 QCString 314 346 #endif 315 f ileName = QFile::encodeName( soundFile );316 347 fName = QFile::encodeName( soundFile ); 348 317 349 MMIOINFO mmioinfo = { 0 }; 318 350 mmioinfo.ulTranslate = MMIO_TRANSLATEDATA | MMIO_TRANSLATEHEADER; 319 fileHandle = mmioOpen( f ileName.data(), &mmioinfo, MMIO_READ );351 fileHandle = mmioOpen( fName.data(), &mmioinfo, MMIO_READ ); 320 352 if ( fileHandle == NULLHANDLE ) { 321 353 #if defined(QT_DEBUG) 322 qDebug( "QAuBucketMMPM: falied to open sound file [%s]", 323 fileName.data() ); 354 qDebug( "QAuBucketMMPM: falied to open sound file [%s]", fName.data() ); 324 355 #endif 325 356 return; … … 332 363 #if defined(QT_DEBUG) 333 364 qDebug( "QAuBucketMMPM: [%s] is not a sound file or " 334 "has an unsupported format (rc=%04ld)", fileName.data(), rc ); 335 #endif 336 return; 337 } 365 "has an unsupported format (rc=%04hu:%04hu)", 366 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) ); 367 #endif 368 mmioClose( fileHandle, 0 ); 369 fileHandle = NULLHANDLE; 370 return; 371 } 372 373 srv->bucketList.append( this ); 374 375 #if defined(QT_QSOUND_DEBUG) 376 qDebug( "QAuBucketMMPM::init(): {%p} [%s]", this, fName.data() ); 377 #endif 378 } 379 380 QAuBucketMMPM::~QAuBucketMMPM() 381 { 382 #if defined(QT_QSOUND_DEBUG) 383 qDebug( "~QAuBucketMMPM(): {%p} [%s]", this, fName.data() ); 384 #endif 385 386 if ( deviceId ) 387 close( srv->hwnd == NULLHANDLE ); 388 389 if ( fileHandle != NULLHANDLE ) { 390 srv->bucketList.removeRef( this ); 391 mmioClose( fileHandle, 0 ); 392 } 393 } 394 395 void QAuBucketMMPM::open() 396 { 397 Q_ASSERT( !deviceId ); 398 if ( deviceId ) 399 return; 338 400 339 401 MCI_OPEN_PARMS openParams = { 0 }; 402 openParams.hwndCallback = srv->hwnd; 340 403 openParams.pszDeviceType = (PSZ) MAKEULONG( MCI_DEVTYPE_WAVEFORM_AUDIO, 0 ); 341 404 openParams.pszElementName = (PSZ) fileHandle; 342 rc = mciSendCommand( 0, MCI_OPEN, MCI_WAIT | MCI_OPEN_MMIO|343 344 &openParams, 0 );405 ULONG rc = mciSendCommand( 0, MCI_OPEN, MCI_WAIT | MCI_OPEN_MMIO | MCI_READONLY | 406 MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID, 407 &openParams, 0 ); 345 408 if ( rc != MCIERR_SUCCESS ) { 346 409 #if defined(QT_DEBUG) 347 410 qDebug( "QAuBucketMMPM: failed to open a device for sound file [%s] " 348 "(rc=%04 ld)", fileName.data(), rc);411 "(rc=%04hu:%04hu)", fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) ); 349 412 #endif 350 413 return; … … 352 415 353 416 deviceId = openParams.usDeviceID; 354 } 355 356 QAuBucketMMPM::~QAuBucketMMPM() 357 { 417 418 srv->bucketMap.insert( deviceId, this ); 419 358 420 #if defined(QT_QSOUND_DEBUG) 359 qDebug( "~QAuBucketMMPM(): this=%p", this ); 421 qDebug( "QAuBucketMMPM::open(): {%p} [%s] deviceId=%08hu", 422 this, fName.data(), deviceId ); 423 #endif 424 } 425 426 void QAuBucketMMPM::close( bool atExit /* = FALSE */) 427 { 428 Q_ASSERT( deviceId ); 429 if ( !deviceId ) 430 return; 431 432 #if defined(QT_QSOUND_DEBUG) 433 qDebug( "QAuBucketMMPM()::close(): {%p} [%s]", this, fName.data() ); 360 434 #endif 361 435 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 380 void QAuBucketMMPM::play() 381 { 436 // remove from the map anyway -- we don't plan to retry 437 srv->bucketMap.remove( deviceId ); 438 439 // Use MCI_CLOSE_EXIT to tell the media control driver it should 440 // close immediately, w/o doing for any notifications, etc. 441 ULONG param = atExit ? MCI_CLOSE_EXIT : 0; 442 443 ULONG rc = mciSendCommand( deviceId, MCI_CLOSE, param, NULL, 0 ); 444 #if defined(QT_DEBUG) 445 if ( rc != MCIERR_SUCCESS ) 446 qDebug( "QAuBucketMMPM: failed to close the device for sound file [%s] " 447 "(rc=%04hu:%04hu)", 448 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) ); 449 #endif 450 Q_UNUSED( rc ); 451 452 st = Stopped; 453 deviceId = 0; 454 } 455 456 bool QAuBucketMMPM::play() 457 { 458 #if defined(QT_QSOUND_DEBUG) 459 qDebug( "QAuBucketMMPM()::play(): {%p} [%s]", this, fName.data() ); 460 #endif 461 462 if ( !deviceId ) { 463 open(); 464 if ( !deviceId ) 465 return FALSE; 466 } 467 382 468 MCI_PLAY_PARMS playParams = { 0 }; 383 469 playParams.hwndCallback = srv->hwnd; 384 470 playParams.ulFrom = 0; // always play from the beginning 385 471 386 472 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 } 473 &playParams, 0 ); 474 475 if ( LOUSHORT( rc ) == MCIERR_INSTANCE_INACTIVE ) { 476 // There are not enough simultaneous audio streams. Try to acquire the 477 // resources and play again. Note that if the device is already acquired 478 // for exclusive use, this command will not wait but return immediately. 479 rc = mciSendCommand( deviceId, MCI_ACQUIREDEVICE, MCI_WAIT, NULL, 0 ); 480 if ( rc == MCIERR_SUCCESS ) { 481 rc = mciSendCommand( deviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM, 482 &playParams, 0 ); 483 } else if ( LOUSHORT( rc ) == MCIERR_DEVICE_LOCKED && 484 snd && snd->loops() < 0 ) { 485 // Enter the special state to let this infinitive sound start 486 // playing when the resource becomes available. 487 st = Waiting; 488 return FALSE; 489 } 490 } 491 492 if ( rc == MCIERR_SUCCESS ) { 493 st = Playing; 494 } else { 495 st = Stopped; 496 #if defined(QT_DEBUG) 497 qDebug( "QAuBucketMMPM: failed to play sound file [%s] (rc=%04hu:%04hu)", 498 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) ); 499 #endif 500 } 501 502 return st == Playing; 395 503 } 396 504 397 505 void QAuBucketMMPM::stop() 398 506 { 507 if ( st == Stopped ) 508 return; 509 510 // always go to Stopped -- we won't retry 511 // (this is also used in MM_MCINOTIFY processing) 512 st = Stopped; 513 399 514 ULONG rc = mciSendCommand( deviceId, MCI_STOP, MCI_WAIT, NULL, 0 ); 400 515 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 516 if ( rc == MCIERR_SUCCESS ) { 517 // nothing 518 } else if ( LOUSHORT( rc ) == MCIERR_INSTANCE_INACTIVE ) { 519 // The infinite QSound-full bucket is now suspended (some other instance 520 // has gained the resource). Close this instance to prevent it from 521 // being automatically resumed later. 522 close(); 523 } else { 524 #if defined(QT_DEBUG) 525 qDebug( "QAuBucketMMPM: failed to stop sound file [%s] (rc=%04hu:%04hu)", 526 fName.data(), HIUSHORT( rc ), LOUSHORT( rc ) ); 527 #endif 528 // last chance to stop 529 close(); 530 } 531 } 532 533 void QAuBucketMMPM::onDeviceGained( bool gained ) 534 { 535 if ( gained ) { 536 // We gained the device resource. 537 if ( st == Waiting ) { 538 /// @todo (dmik) For some reason, starting playback from here 539 // (i.e. when we've been given the deivce resource back after 540 // some other application finished using it exclusively), we can 541 // get error 5053 (MCIERR_NO_CONNECTION) or even undocumented 5659. 542 // When it happens, subsequent attempts to play something will 543 // result into an unkillable hang... Experimemtal for now. 544 #if 1 545 // A QSound-full bucket attempted to play eventually regained 546 // the resource. Start playing. 547 play(); 548 #else 549 st = Stopped; 550 #endif 551 } 552 } else { 553 // We lost the device resource. 554 if ( st == Playing ) { 555 // Close the instance to prevent the playback from being resumed 556 // when the device is auto-gained again (by another instance that 557 // uses MCI_RETURN_RESOURCE in MCI_RELEASEDEVICE). The exception is 558 // a QSound-full bucket with an infinitive loop (that will continue 559 // playing when the resuorce is back). 560 if ( snd ) { 561 // infinitive loop? 562 if ( snd->loops() < 0 ) 563 return; 564 // decrease loops to zero 565 while ( srv->decLoop( snd ) > 0 ) ; 566 } 567 close(); 568 if ( !snd ) { 569 // delete QSound-less bucket 570 delete this; 571 } 572 } 406 573 } 407 574 } … … 413 580 QAuServerMMPM::QAuServerMMPM( QObject* parent ) : 414 581 QAuServer( parent, "OS/2 MMPM Audio Server" ), 415 isOk( FALSE ), isAboutToQuit( FALSE ),582 hwnd( NULLHANDLE ), isOk( FALSE ), 416 583 mdmLib( "mdm.dll" ), mmioLib( "mmio.dll" ) 417 584 { … … 450 617 #if defined(QT_DEBUG) 451 618 qDebug( "QAuServerMMPM: failed to open the default waveaudio device " 452 "(rc=%04 ld)", rc);619 "(rc=%04hu:%04hu)", HIUSHORT( rc ), LOUSHORT( rc ) ); 453 620 #endif 454 621 return; … … 458 625 mciSendCommand( openParams.usDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0 ); 459 626 460 connect( qApp, SIGNAL( aboutToQuit() ), SLOT( aboutToQuit() ) );627 connect( qApp, SIGNAL( aboutToQuit() ), SLOT( serverUninit() ) ); 461 628 462 629 isOk = TRUE; … … 465 632 QAuServerMMPM::~QAuServerMMPM() 466 633 { 634 // still alive? 635 if ( hwnd ) 636 serverUninit(); 637 } 638 639 void QAuServerMMPM::serverUninit() 640 { 467 641 #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(); 642 qDebug( "QAuServerMMPM::serverUninit(): buckets left: %d", 643 bucketList.count() ); 644 #endif 645 646 Q_ASSERT( hwnd ); 647 if ( hwnd == NULLHANDLE ) 648 return; 649 650 WinDestroyWindow( hwnd ); 651 hwnd = NULLHANDLE; 652 653 // Deassociate all remaining buckets from QSound objects and delete them 654 // (note that deleting a bucket will remove it from the list, which 655 // in turn will advance the current item to the next one, so current() is 656 // is used instead of next() in the loop). 657 for ( QAuBucketMMPM *b = bucketList.first(); b; b = bucketList.current() ) { 658 QSound *s = b->sound(); 478 659 if ( s ) { 479 660 // the below call will delete the associated bucket 480 661 setBucket ( s, 0 ); 481 662 } else { 482 delete it.current();663 delete b; 483 664 } 484 665 } 485 666 486 Q_ASSERT( buckets.count() == 0 ); 487 667 Q_ASSERT( bucketList.count() == 0 ); 668 Q_ASSERT( bucketMap.count() == 0 ); 669 488 670 mmioClose = 0; 489 671 mmioOpen = 0; … … 503 685 setBucket( s, b ); 504 686 #if defined(QT_QSOUND_DEBUG) 505 qDebug( "init(): bucket=%p sound=[%s]", b, s->fileName().latin1() ); 687 qDebug( "QAuServerMMPM::init(): bucket=%p [%s]", 688 b, b->fileName().data() ); 506 689 #endif 507 690 } else { … … 519 702 if ( b->okay() ) { 520 703 #if defined(QT_QSOUND_DEBUG) 521 qDebug( "play(QString): bucket=%p sound=[%s]", b, filename.latin1() );704 qDebug( "play(QString): bucket=%p [%s]", b, b->fileName().data() ); 522 705 #endif 523 b->play(); 524 // b will be deleted in WindowProc() 706 if ( b->play() ) { 707 // b will be deleted in WindowProc() or in onDeviceGained() 708 } else { 709 delete b; 710 } 525 711 } else { 526 712 delete b; … … 541 727 if ( b ) { 542 728 #if defined(QT_QSOUND_DEBUG) 543 qDebug( "play(QSound): bucket=%p sound=[%s]", b, s->fileName().latin1() );729 qDebug( "play(QSound): bucket=%p [%s]", b, b->fileName().data() ); 544 730 #endif 545 731 b->play(); … … 590 776 USHORT code = SHORT1FROMMP( mp1 ); 591 777 USHORT deviceId = SHORT1FROMMP( mp2 ); 778 USHORT mcimsg = SHORT2FROMMP( mp2 ); 592 779 #if defined(QT_QSOUND_DEBUG) 593 qDebug( "MM_MCINOTIFY: code=%04hX deviceId=%04hX", code, deviceId ); 780 qDebug( "MM_MCINOTIFY: code=0x%04hX, deviceId=%04hu, mcimsg=%04hu", 781 code, deviceId, mcimsg ); 594 782 #endif 595 596 QAuBucketMMPM *b = that->buckets.find( deviceId ); 783 Q_ASSERT( mcimsg == MCI_PLAY ); 784 if ( mcimsg != MCI_PLAY ) 785 return 0; 786 787 QAuBucketMMPM *b = that->bucketMap.find( deviceId ); 788 // There will be a late notification if the sound is 789 // playing when close() happens. Just return silently. 597 790 if ( !b ) 598 791 return 0; 792 599 793 #if defined(QT_QSOUND_DEBUG) 600 qDebug( "MM_MCINOTIFY: bucket=%p ", b);794 qDebug( "MM_MCINOTIFY: bucket=%p [%s]", b, b->fileName().data() ); 601 795 #endif 602 796 603 797 QSound *sound = b->sound(); 604 798 if ( sound ) { 605 if ( code == MCI_NOTIFY_SUCCESSFUL ) { 799 bool returnResource = FALSE; 800 if ( b->state() == QAuBucketMMPM::Stopped ) { 801 // It's possible that MCI_STOP is issued right after MCI_PLAY 802 // has successfilly finished and sent MM_MCINOTIFY but before 803 // we start it again here. Obey the STOP request. 804 returnResource = TRUE; 805 } else if ( code == MCI_NOTIFY_SUCCESSFUL ) { 606 806 // play the sound until there are no loops left 607 807 int loopsLeft = that->decLoop( sound ); 608 808 if ( loopsLeft != 0 ) 609 809 b->play(); 810 else 811 returnResource = TRUE; 812 } else if ( code != MCI_NOTIFY_SUPERSEDED ) { 813 returnResource = TRUE; 814 } 815 if ( returnResource ) { 816 // let infinitive sounds continue playing 817 mciSendCommand( deviceId, MCI_RELEASEDEVICE, 818 MCI_RETURN_RESOURCE, NULL, 0 ); 610 819 } 611 820 } else { 612 // delete QSound'less bucket when finished or stopped playing 821 // delete QSound-less bucket when finished or stopped playing 822 // (closing the instance will return the resource) 613 823 delete b; 614 824 } … … 616 826 return 0; 617 827 } 618 case WM_USER: { 619 HMMIO fileHandle = LONGFROMMP( mp1 ); 620 USHORT deviceId = SHORT1FROMMP( mp2 ); 828 case MM_MCIPASSDEVICE: { 829 if ( !that ) 830 return 0; 831 832 USHORT deviceId = SHORT1FROMMP( mp1 ); 833 USHORT event = SHORT1FROMMP( mp2 ); 621 834 #if defined(QT_QSOUND_DEBUG) 622 qDebug( "WM_USER: deviceId=%04hX", deviceId ); 835 qDebug( "MM_MCIPASSDEVICE: deviceId=%04hu, event=0x%04hX", 836 deviceId, event ); 623 837 #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 ); 838 QAuBucketMMPM *b = that->bucketMap.find( deviceId ); 839 if ( !b ) 840 return 0; 841 842 #if defined(QT_QSOUND_DEBUG) 843 qDebug( "MM_MCIPASSDEVICE: bucket=%p [%s]", b, b->fileName().data() ); 844 #endif 845 // Note: this call may delete b 846 b->onDeviceGained( event == MCI_GAINING_USE ); 847 848 return 0; 628 849 } 629 850 }
Note:
See TracChangeset
for help on using the changeset viewer.