
/*
 *@@sourcefile sound.c:
 *      this is the source code for SOUND.DLL which is used by
 *      XFolder to play the new system sounds introduced with V0.70.
 *      Unfortunately, with V0.82 I found out that the reason that
 *      XFolder wouldn't install on some systems was that the main
 *      XFLDR.DLL imported a few MMPM/2 library functions, and the
 *      OS/2 DLL loader then would refuse to load XFolder if MMPM/2
 *      was not installed. As a result, XFolder class registration
 *      failed (naturally, without any meaningful error messages.
 *      FALSE isn't that meaningful, in my view.)
 *
 *      So this file is new with V0.82. The system sounds are still
 *      played by fnwpQuickObject (xfdesk.c), but all the function
 *      calls to MMPM/2 have been moved into this file so that the main
 *      DLL does not have to be linked against the MMPM/2 libraries.
 *
 *      Note: The functions in this file are not intended to
 *      simply be called to play sounds because access to multimedia
 *      devices needs to be serialized using MMPM/2 messages. This
 *      is done by the Quick thread. Post QM_PLAYSOUND to the
 *      quick thread instead.
 *
 *      The smart thing about this DLL is that if any error occurs
 *      loading the DLL, XFolder will gracefully disable the new
 *      system sounds. That is, if MMPM/2 is not installed (loading
 *      of SOUND.DLL then fails because the imports to MMPM/2 cannot
 *      be resolved) or if SOUND.DLL is simply not found. As a
 *      result, you can simply delete SOUND.DLL to disable sounds.
 *      See xthrInitializeThreads (xthreads.c) where this DLL is loaded.
 *
 *      With V0.82, I have also fixed some problems WRT multiple
 *      applications playing sounds at the same time. Error codes
 *      are now properly checked for and if another application
 *      requests use of the audio device, XFolder will stop its
 *      own playing and release the device.
 *
 *      See the notes in fnwpQuickObject (xfdesk.c) for details about
 *      the interaction between the Quick thread and this DLL and
 *      the MMPM/2 messages involved in this.
 *
 *      Note that the makefile (xfldr.mak) compiles this source
 *      code as a subsystem library to reduce the size of the DLL.
 *      As a result, SOUND.DLL is _not_ thread-safe and must only
 *      be called by the Quick thread.
 *      See the VAC++ docs for more information on subsystem libraries.
 *
 *@@include #define INCL_WINWINDOWMGR
 *@@include #include <os2.h>
 *@@include #include "sound.h"
 */

/*
 *      Copyright (C) 1997-99 Ulrich M”ller.
 *      This file is part of the XFolder source package.
 *      XFolder is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XFolder main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

// basic OS/2 includes
#include <os2.h>

// multimedia includes
#define INCL_MCIOS2
#define INCL_OS2MM
#include <os2me.h>

// #define _PMPRINTF_
#include "pmprintf.h"

#include <string.h>

/*
 *@@ sndOpenSound:
 *      this is called by the Quick thread (fnwpQuickObject)
 *      when it receives QM_PLAYSOUND to start playing a sound.
 *      In order to store the current status of
 *      the sound device, the Quick thread uses a global
 *      usDeviceID variable in xthreads.c, which is modified
 *      by this func. "Device" in this context means an
 *      individual sound file.
 *
 *      We also need the object window of the calling
 *      thread (which is hwndQuickObject from xthreads.c)
 *      to inform MMPM/2 where to post notification
 *      messages.
 *
 *      We will only attempt to open the sound file here
 *      and then return; we will _not_ yet play the sound
 *      because we will need to wait for the MM_MCIPASSDEVICE
 *      message (which will be posted to hwndQuickObject, the Quick
 *      thread object window) for checking whether the device
 *      is actually available.
 *
 *      Note: This function is _not_ prototyped in sound.h
 *      because its address is resolved dynamically by
 *      xthrInitializeThreads.
 */

VOID extern sndOpenSound(HWND hwndObject,       // in: Quick thread object wnd
                         PUSHORT pusDeviceID,   // in/out: "device" ID (= sound file).
                                // This is != 0 if we're already playing something
                         PSZ pszFile)           // in: sound file to play
{
    ULONG           ulrc;
    MCI_OPEN_PARMS  mop;
    MCI_GENERIC_PARMS mgp;

    if (*pusDeviceID)
    {
        // if the device ID is != 0, that means
        // that we're already playing: abort
        // current job and close device first
        mgp.hwndCallback = hwndObject;
        mciSendCommand(*pusDeviceID,
                    MCI_STOP,
                    MCI_WAIT,
                    &mgp,
                    0);
        mciSendCommand(*pusDeviceID,
                    MCI_CLOSE,
                    MCI_WAIT,
                    &mgp,
                    0);
        _Pmpf(("SOUND.DLL: Stopped old sound"));
    }

    _Pmpf(("SOUND.DLL: Opening sound file"));
    // open new device
    memset(&mop, 0, sizeof(mop));
    mop.hwndCallback = hwndObject;       // callback hwnd
    // mop.usDeviceID = 0;                  // we don't have one yet
    // mop.pszDeviceType = NULL;            // using default device type
    mop.pszElementName = pszFile;        // file name to open

    // now we open the file; note the flags:
    // --  MCI_READONLY is important for two reasons:
    //     1)   otherwise, if the file doesn't exist, MMPM/2
    //          will create a temporary file for writing into,
    //          which costs too much time;
    //     2)   if opening fails, the file would be blocked for
    //          the rest of this session.
    // --  MCI_OPEN_SHAREABLE allows us to share the device
    //     with other applications and get MM_MCIPASSDEVICE msgs.
    // From now on, the sound file will be our "device".
    ulrc = mciSendCommand(0,
                MCI_OPEN,
                MCI_WAIT | MCI_OPEN_ELEMENT | MCI_READONLY | MCI_OPEN_SHAREABLE,
                &mop,
                0);                              // No user parm

    if (LOUSHORT(ulrc) == MCIERR_SUCCESS) {
        // successful: go on
        *pusDeviceID = mop.usDeviceID;     // remember device ID

        _Pmpf(("  SOUND.DLL: Device opened"));
    } else {
        // no success:
        // we need to close the device again then, because
        // otherwise we won't be able to play that sound again
        // (remember: "device" is the sound file here)
        mciSendCommand(mop.usDeviceID,
                    MCI_CLOSE,
                    MCI_WAIT,
                    &mgp,
                    0);
        #ifdef _PMPRINTF_
        {
            CHAR szError[1000];
            mciGetErrorString(ulrc, szError, sizeof(szError));
            _Pmpf(("  DeviceID %d has error %d (\"%s\")",
                        mop.usDeviceID, ulrc, szError));
        }
        #endif
        *pusDeviceID = 0;
    }
}

/*
 *@@ sndPlaySound:
 *      this is called by the Quick thread (fnwpQuickObject)
 *      when it receives MM_MCIPASSDEVICE with MCI_GAINING_USE
 *      set, i.e. the device is ready to play. So that's
 *      what we'll do here.
 *
 *      Note: This function is _not_ prototyped in sound.h
 *      because its address is resolved dynamically by
 *      xthrInitializeThreads.
 */

VOID extern sndPlaySound(HWND hwndObject,     // in: Quick thread object wnd
                         PUSHORT pusDeviceID, // in: "device" ID (= sound file)
                         ULONG ulVolume)      // in: volume for sound (0-100)
{
    MCI_PLAY_PARMS  mpp;
    MCI_SET_PARMS   msp;

    _Pmpf(("  SOUND.DLL: Playing sound"));

    // set the volume for this sound
    msp.ulLevel = ulVolume;
    msp.ulAudio = MCI_SET_AUDIO_ALL;
    mciSendCommand(*pusDeviceID,
                   MCI_SET,
                   MCI_WAIT | MCI_SET_AUDIO |
                   MCI_SET_VOLUME,
                   &msp, 0);

    // play and request MM_MCINOTIFY msg to
    // the Quick thread object window
    mpp.hwndCallback = (HWND)hwndObject;
    mciSendCommand(*pusDeviceID,
                    MCI_PLAY,
                    MCI_NOTIFY,
                    (PVOID)&mpp,
                    0);
}

/*
 *@@ sndStopSound:
 *      this is called by the Quick thread (fnwpQuickObject)
 *      in two situations:
 *      1)  MMPM/2 is done playing our sound, i.e.
 *          upon receiving MM_MCINOTIFY;
 *      2)  another application requests the waveform
 *          device for playing, i.e. upon receiving
 *          MM_MCIPASSDEVICE with MCI_LOSING_USE set.
 *
 *      In both situations, we need to close our
 *      device.
 *
 *      Note: This function is _not_ prototyped in sound.h
 *      because its address is resolved dynamically by
 *      xthrInitializeThreads.
 */

VOID extern sndStopSound(PUSHORT pusDeviceID)
{
    MCI_GENERIC_PARMS mgp;
    // stop playing the sound (this will probably do
    // nothing if the sound is already done with)
    mciSendCommand(*pusDeviceID,
                MCI_STOP,
                MCI_WAIT,
                &mgp,
                0);
    // close the device
    mciSendCommand(*pusDeviceID,
                MCI_CLOSE,
                MCI_WAIT,
                &mgp,
                0);
    // set the Quick thread's device ID to 0 so
    // we know that we're not currently playing
    // anything
    *pusDeviceID = 0;
}

