Ignore:
Timestamp:
Oct 19, 2009, 9:12:20 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: Ported QSound to OS/2 using the MMPM backend.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/kernel/qsound_pm.cpp

    r95 r250  
    4949#include "qapplication_p.h"
    5050#include <qfile.h>
    51 #include "qpointer.h"
    5251#include "qsound_p.h"
    5352
     53#include "qlist.h"
     54#include "qhash.h"
     55#include "qlibrary.h"
     56
    5457#include <qt_os2.h>
    5558
     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
    5668QT_BEGIN_NAMESPACE
    5769
     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
     80typedef ULONG HMMIO;                  /* Handle to an MMIO object*/
     81
     82typedef ULONG  FOURCC;
     83
     84typedef 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
     90typedef MMIOPROC FAR *PMMIOPROC;
     91
     92typedef 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
     112typedef MMIOINFO FAR *PMMIOINFO;
     113
     114typedef 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
     123typedef 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
     129typedef struct _MMXWAV_HEADER {              /* mmxwaveheader            */
     130   WAVE_HEADER     WAVEHeader;               /* Per RIFF WAVE Definition */
     131   XWAV_HEADERINFO XWAVHeaderInfo;           /* Extended wave definition */
     132   } MMXWAV_HEADER;
     133
     134typedef 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
     141typedef MMAUDIOHEADER *PMMAUDIOHEADER;
     142
     143typedef 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;
     152typedef MCI_OPEN_PARMS   *PMCI_OPEN_PARMS;
     153
     154typedef 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;
     160typedef 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
     209typedef
     210USHORT (APIENTRY *mmioClose_T)(HMMIO hmmio,
     211                               USHORT usFlags);
     212typedef
     213HMMIO (APIENTRY *mmioOpen_T)(PSZ pszFileName,
     214                             PMMIOINFO pmmioinfo,
     215                             ULONG ulOpenFlags);
     216
     217typedef
     218ULONG (APIENTRY *mmioGetHeader_T)(HMMIO hmmio,
     219                                  PVOID pHeader,
     220                                  LONG lHeaderLength,
     221                                  PLONG plBytesRead,
     222                                  ULONG ulReserved,
     223                                  ULONG ulFlags);
     224
     225static mmioClose_T mmioClose = 0;
     226static mmioOpen_T mmioOpen = 0;
     227static mmioGetHeader_T mmioGetHeader = 0;
     228
     229// functions resolved by mdm.dll
     230
     231typedef
     232ULONG (APIENTRY *mciSendCommand_T)(USHORT   usDeviceID,
     233                                   USHORT   usMessage,
     234                                   ULONG    ulParam1,
     235                                   PVOID    pParam2,
     236                                   USHORT   usUserParm);
     237
     238static mciSendCommand_T mciSendCommand = 0;
     239
     240////////////////////////////////////////////////////////////////////////////////
     241
     242class QAuBucketMMPM;
     243
     244class QAuServerMMPM : public QAuServer {
     245
     246    Q_OBJECT
     247
     248public:
     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
     259private slots:
     260
     261    void serverUninit();
     262
     263private:
     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
     283class QAuBucketMMPM : public QAuBucket {
     284
     285public:
     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
     308private:
     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
     327QAuBucketMMPM::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
     339QAuBucketMMPM::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
     350void 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
     392QAuBucketMMPM::~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
     409void 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
     460void 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
     491bool 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
     540void 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
     568void 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
     613const char *QAuServerMMPM::ClassName = "QAuServerMMPM";
     614
     615QAuServerMMPM::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
     664QAuServerMMPM::~QAuServerMMPM()
     665{
     666    // still alive?
     667    if (hwnd)
     668        serverUninit();
     669}
     670
     671void 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
     709void 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
     729void 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
     749void 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
     771void 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
     783bool QAuServerMMPM::okay()
     784{
     785    return isOk;
     786}
     787
     788MRESULT 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
    58888QAuServer* qt_new_audio_server()
    59889{
    60     // @todo implement
    61     return 0;
     890    return new QAuServerMMPM(qApp);
    62891}
    63892
    64893QT_END_NAMESPACE
    65894
    66 // @todo
    67 //#include "qsound_pm.moc"
     895#include "qsound_pm.moc"
    68896
    69897#endif // QT_NO_SOUND
Note: See TracChangeset for help on using the changeset viewer.