home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: WPS_PM / WPS_PM.zip / xfld085s.zip / main / sound.c < prev    next >
C/C++ Source or Header  |  1999-02-23  |  10KB  |  266 lines

  1.  
  2. /*
  3.  *@@sourcefile sound.c:
  4.  *      this is the source code for SOUND.DLL which is used by
  5.  *      XFolder to play the new system sounds introduced with V0.70.
  6.  *      Unfortunately, with V0.82 I found out that the reason that
  7.  *      XFolder wouldn't install on some systems was that the main
  8.  *      XFLDR.DLL imported a few MMPM/2 library functions, and the
  9.  *      OS/2 DLL loader then would refuse to load XFolder if MMPM/2
  10.  *      was not installed. As a result, XFolder class registration
  11.  *      failed (naturally, without any meaningful error messages.
  12.  *      FALSE isn't that meaningful, in my view.)
  13.  *
  14.  *      So this file is new with V0.82. The system sounds are still
  15.  *      played by fnwpQuickObject (xfdesk.c), but all the function
  16.  *      calls to MMPM/2 have been moved into this file so that the main
  17.  *      DLL does not have to be linked against the MMPM/2 libraries.
  18.  *
  19.  *      Note: The functions in this file are not intended to
  20.  *      simply be called to play sounds because access to multimedia
  21.  *      devices needs to be serialized using MMPM/2 messages. This
  22.  *      is done by the Quick thread. Post QM_PLAYSOUND to the
  23.  *      quick thread instead.
  24.  *
  25.  *      The smart thing about this DLL is that if any error occurs
  26.  *      loading the DLL, XFolder will gracefully disable the new
  27.  *      system sounds. That is, if MMPM/2 is not installed (loading
  28.  *      of SOUND.DLL then fails because the imports to MMPM/2 cannot
  29.  *      be resolved) or if SOUND.DLL is simply not found. As a
  30.  *      result, you can simply delete SOUND.DLL to disable sounds.
  31.  *      See xthrInitializeThreads (xthreads.c) where this DLL is loaded.
  32.  *
  33.  *      With V0.82, I have also fixed some problems WRT multiple
  34.  *      applications playing sounds at the same time. Error codes
  35.  *      are now properly checked for and if another application
  36.  *      requests use of the audio device, XFolder will stop its
  37.  *      own playing and release the device.
  38.  *
  39.  *      See the notes in fnwpQuickObject (xfdesk.c) for details about
  40.  *      the interaction between the Quick thread and this DLL and
  41.  *      the MMPM/2 messages involved in this.
  42.  *
  43.  *      Note that the makefile (xfldr.mak) compiles this source
  44.  *      code as a subsystem library to reduce the size of the DLL.
  45.  *      As a result, SOUND.DLL is _not_ thread-safe and must only
  46.  *      be called by the Quick thread.
  47.  *      See the VAC++ docs for more information on subsystem libraries.
  48.  *
  49.  *@@include #define INCL_WINWINDOWMGR
  50.  *@@include #include <os2.h>
  51.  *@@include #include "sound.h"
  52.  */
  53.  
  54. /*
  55.  *      Copyright (C) 1997-99 Ulrich Möller.
  56.  *      This file is part of the XFolder source package.
  57.  *      XFolder is free software; you can redistribute it and/or modify
  58.  *      it under the terms of the GNU General Public License as published
  59.  *      by the Free Software Foundation, in version 2 as it comes in the
  60.  *      "COPYING" file of the XFolder main distribution.
  61.  *      This program is distributed in the hope that it will be useful,
  62.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  63.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  64.  *      GNU General Public License for more details.
  65.  */
  66.  
  67. // basic OS/2 includes
  68. #include <os2.h>
  69.  
  70. // multimedia includes
  71. #define INCL_MCIOS2
  72. #define INCL_OS2MM
  73. #include <os2me.h>
  74.  
  75. // #define _PMPRINTF_
  76. #include "pmprintf.h"
  77.  
  78. #include <string.h>
  79.  
  80. /*
  81.  *@@ sndOpenSound:
  82.  *      this is called by the Quick thread (fnwpQuickObject)
  83.  *      when it receives QM_PLAYSOUND to start playing a sound.
  84.  *      In order to store the current status of
  85.  *      the sound device, the Quick thread uses a global
  86.  *      usDeviceID variable in xthreads.c, which is modified
  87.  *      by this func. "Device" in this context means an
  88.  *      individual sound file.
  89.  *
  90.  *      We also need the object window of the calling
  91.  *      thread (which is hwndQuickObject from xthreads.c)
  92.  *      to inform MMPM/2 where to post notification
  93.  *      messages.
  94.  *
  95.  *      We will only attempt to open the sound file here
  96.  *      and then return; we will _not_ yet play the sound
  97.  *      because we will need to wait for the MM_MCIPASSDEVICE
  98.  *      message (which will be posted to hwndQuickObject, the Quick
  99.  *      thread object window) for checking whether the device
  100.  *      is actually available.
  101.  *
  102.  *      Note: This function is _not_ prototyped in sound.h
  103.  *      because its address is resolved dynamically by
  104.  *      xthrInitializeThreads.
  105.  */
  106.  
  107. VOID extern sndOpenSound(HWND hwndObject,       // in: Quick thread object wnd
  108.                          PUSHORT pusDeviceID,   // in/out: "device" ID (= sound file).
  109.                                 // This is != 0 if we're already playing something
  110.                          PSZ pszFile)           // in: sound file to play
  111. {
  112.     ULONG           ulrc;
  113.     MCI_OPEN_PARMS  mop;
  114.     MCI_GENERIC_PARMS mgp;
  115.  
  116.     if (*pusDeviceID)
  117.     {
  118.         // if the device ID is != 0, that means
  119.         // that we're already playing: abort
  120.         // current job and close device first
  121.         mgp.hwndCallback = hwndObject;
  122.         mciSendCommand(*pusDeviceID,
  123.                     MCI_STOP,
  124.                     MCI_WAIT,
  125.                     &mgp,
  126.                     0);
  127.         mciSendCommand(*pusDeviceID,
  128.                     MCI_CLOSE,
  129.                     MCI_WAIT,
  130.                     &mgp,
  131.                     0);
  132.         _Pmpf(("SOUND.DLL: Stopped old sound"));
  133.     }
  134.  
  135.     _Pmpf(("SOUND.DLL: Opening sound file"));
  136.     // open new device
  137.     memset(&mop, 0, sizeof(mop));
  138.     mop.hwndCallback = hwndObject;       // callback hwnd
  139.     // mop.usDeviceID = 0;                  // we don't have one yet
  140.     // mop.pszDeviceType = NULL;            // using default device type
  141.     mop.pszElementName = pszFile;        // file name to open
  142.  
  143.     // now we open the file; note the flags:
  144.     // --  MCI_READONLY is important for two reasons:
  145.     //     1)   otherwise, if the file doesn't exist, MMPM/2
  146.     //          will create a temporary file for writing into,
  147.     //          which costs too much time;
  148.     //     2)   if opening fails, the file would be blocked for
  149.     //          the rest of this session.
  150.     // --  MCI_OPEN_SHAREABLE allows us to share the device
  151.     //     with other applications and get MM_MCIPASSDEVICE msgs.
  152.     // From now on, the sound file will be our "device".
  153.     ulrc = mciSendCommand(0,
  154.                 MCI_OPEN,
  155.                 MCI_WAIT | MCI_OPEN_ELEMENT | MCI_READONLY | MCI_OPEN_SHAREABLE,
  156.                 &mop,
  157.                 0);                              // No user parm
  158.  
  159.     if (LOUSHORT(ulrc) == MCIERR_SUCCESS) {
  160.         // successful: go on
  161.         *pusDeviceID = mop.usDeviceID;     // remember device ID
  162.  
  163.         _Pmpf(("  SOUND.DLL: Device opened"));
  164.     } else {
  165.         // no success:
  166.         // we need to close the device again then, because
  167.         // otherwise we won't be able to play that sound again
  168.         // (remember: "device" is the sound file here)
  169.         mciSendCommand(mop.usDeviceID,
  170.                     MCI_CLOSE,
  171.                     MCI_WAIT,
  172.                     &mgp,
  173.                     0);
  174.         #ifdef _PMPRINTF_
  175.         {
  176.             CHAR szError[1000];
  177.             mciGetErrorString(ulrc, szError, sizeof(szError));
  178.             _Pmpf(("  DeviceID %d has error %d (\"%s\")",
  179.                         mop.usDeviceID, ulrc, szError));
  180.         }
  181.         #endif
  182.         *pusDeviceID = 0;
  183.     }
  184. }
  185.  
  186. /*
  187.  *@@ sndPlaySound:
  188.  *      this is called by the Quick thread (fnwpQuickObject)
  189.  *      when it receives MM_MCIPASSDEVICE with MCI_GAINING_USE
  190.  *      set, i.e. the device is ready to play. So that's
  191.  *      what we'll do here.
  192.  *
  193.  *      Note: This function is _not_ prototyped in sound.h
  194.  *      because its address is resolved dynamically by
  195.  *      xthrInitializeThreads.
  196.  */
  197.  
  198. VOID extern sndPlaySound(HWND hwndObject,     // in: Quick thread object wnd
  199.                          PUSHORT pusDeviceID, // in: "device" ID (= sound file)
  200.                          ULONG ulVolume)      // in: volume for sound (0-100)
  201. {
  202.     MCI_PLAY_PARMS  mpp;
  203.     MCI_SET_PARMS   msp;
  204.  
  205.     _Pmpf(("  SOUND.DLL: Playing sound"));
  206.  
  207.     // set the volume for this sound
  208.     msp.ulLevel = ulVolume;
  209.     msp.ulAudio = MCI_SET_AUDIO_ALL;
  210.     mciSendCommand(*pusDeviceID,
  211.                    MCI_SET,
  212.                    MCI_WAIT | MCI_SET_AUDIO |
  213.                    MCI_SET_VOLUME,
  214.                    &msp, 0);
  215.  
  216.     // play and request MM_MCINOTIFY msg to
  217.     // the Quick thread object window
  218.     mpp.hwndCallback = (HWND)hwndObject;
  219.     mciSendCommand(*pusDeviceID,
  220.                     MCI_PLAY,
  221.                     MCI_NOTIFY,
  222.                     (PVOID)&mpp,
  223.                     0);
  224. }
  225.  
  226. /*
  227.  *@@ sndStopSound:
  228.  *      this is called by the Quick thread (fnwpQuickObject)
  229.  *      in two situations:
  230.  *      1)  MMPM/2 is done playing our sound, i.e.
  231.  *          upon receiving MM_MCINOTIFY;
  232.  *      2)  another application requests the waveform
  233.  *          device for playing, i.e. upon receiving
  234.  *          MM_MCIPASSDEVICE with MCI_LOSING_USE set.
  235.  *
  236.  *      In both situations, we need to close our
  237.  *      device.
  238.  *
  239.  *      Note: This function is _not_ prototyped in sound.h
  240.  *      because its address is resolved dynamically by
  241.  *      xthrInitializeThreads.
  242.  */
  243.  
  244. VOID extern sndStopSound(PUSHORT pusDeviceID)
  245. {
  246.     MCI_GENERIC_PARMS mgp;
  247.     // stop playing the sound (this will probably do
  248.     // nothing if the sound is already done with)
  249.     mciSendCommand(*pusDeviceID,
  250.                 MCI_STOP,
  251.                 MCI_WAIT,
  252.                 &mgp,
  253.                 0);
  254.     // close the device
  255.     mciSendCommand(*pusDeviceID,
  256.                 MCI_CLOSE,
  257.                 MCI_WAIT,
  258.                 &mgp,
  259.                 0);
  260.     // set the Quick thread's device ID to 0 so
  261.     // we know that we're not currently playing
  262.     // anything
  263.     *pusDeviceID = 0;
  264. }
  265.  
  266.