Changeset 250 for trunk/src


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.

Location:
trunk/src/gui/kernel
Files:
3 edited

Legend:

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

    r2 r250  
    8787public:
    8888    QSoundPrivate(const QString& fname)
    89         : filename(fname), bucket(0), looprem(0), looptotal(1)
     89        : filename(fname), server(0), bucket(0), looprem(0), looptotal(1)
    9090    {
    9191    }
     
    9797
    9898    QString filename;
     99    QAuServer* server;
    99100    QAuBucket* bucket;
    100101    int looprem;
     
    148149    \row
    149150    \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,
    151152    are supported by Qt for Mac OS X.
    152153    \row
     
    183184    : QObject(*new QSoundPrivate(filename), parent)
    184185{
    185     server().init(this);
     186    d_func()->server = &server();
     187    d_func()->server->init(this);
    186188}
    187189
     
    338340
    339341/*!
     342    Returns true if sound \a s was initialized using this server instance
     343    and false otherwise.
     344*/
     345bool QAuServer::isRelevant(QSound* s)
     346{
     347    return s->d_func()->server == this;
     348}
     349
     350/*!
    340351    Sets the internal bucket record of sound \a s to \a b, deleting
    341352    any previous setting.
  • trunk/src/gui/kernel/qsound_p.h

    r2 r250  
    8989
    9090protected:
     91    bool isRelevant(QSound*);
    9192    void setBucket(QSound*, QAuBucket*);
    9293    QAuBucket* bucket(QSound*);
  • 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.