home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / warptlk3.zip / TOOLKIT / SAMPLES / MM / DUET2 / DUET2.C next >
C/C++ Source or Header  |  1995-10-11  |  75KB  |  1,924 lines

  1. /*************************************************************************
  2.  * File Name    :  DUET2.C
  3.  *
  4.  * Description  :  This file contains the C source code required for the
  5.  *                 DUET2 sample program.
  6.  *
  7.  * Concepts     :  Two parts of a duet will be played as a non-piecemeal group.
  8.  *                 One part will be streamed from a waveform file and the other
  9.  *                 part will be played from an audio CD.  Also, an audio help
  10.  *                 message will be incorporated into IPF help.
  11.  *
  12.  * MMPM/2 API's :  List of all MMPM/2 API's that are used in
  13.  *                 this module
  14.  *
  15.  *                 mciSendCommand    MCI_OPEN
  16.  *                                   MCI_PLAY
  17.  *                                   MCI_PAUSE
  18.  *                                   MCI_RESUME
  19.  *                                   MCI_CLOSE
  20.  *                                   MCI_SET
  21.  *                                   MCI_GROUP
  22.  *                 mciGetErrorString
  23.  *                 mmioOpen
  24.  *                 mmioClose
  25.  *
  26.  * Required
  27.  *    Files     :  duet2.c        Source Code.
  28.  *                 duet2.h        Include file.
  29.  *                 duet2.dlg      Dialog definition.
  30.  *                 duet2.rc       Resources.
  31.  *                 duet2.ipf      Help text.
  32.  *                 duet2.mak      Make file.
  33.  *                 duet2.def      Linker definition file.
  34.  *                 duet2.ico      Program icon.
  35.  *
  36.  * Copyright (C) IBM 1991, 1992, 1993
  37.  *************************************************************************/
  38. #define  INCL_OS2MM                 /* required for MCI and MMIO headers   */
  39. #define  INCL_WIN                   /* required to use Win APIs.           */
  40. #define  INCL_PM                    /* required to use PM APIs.            */
  41. #define  INCL_WINHELP               /* required to use IPF.                */
  42. #define  INCL_WINSTDSLIDER          /* required for using slider control   */
  43. #define  INCL_SECONDARYWINDOW       /* required for secondary window       */
  44. #define  INCL_CIRCULARSLIDER        /* required for circular slider control*/
  45. #define  INCL_GRAPHICBUTTON         /* required for graphic button control */
  46. #define  INCL_MACHDR                /* required for mciplayfile            */
  47.  
  48. #include <os2.h>
  49. #include <os2me.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52.  
  53. #include <sw.h>
  54.  
  55. #include "duet2.h"
  56.  
  57.  
  58. typedef struct
  59.    {
  60.    CHAR  achTitle[LEN_DUET_TITLE];     /* the duet's title                 */
  61.    CHAR  achPart1[LEN_AUDIO_FILENAME]; /* the filename of one duet part    */
  62.    CHAR  achPart2[LEN_AUDIO_FILENAME]; /* the filename of the other part   */
  63.    }DUET;
  64.  
  65. enum DuetStates {ST_CLOSED, ST_STOPPED, ST_PLAYING, ST_PAUSED};
  66.  
  67. /*
  68.  * Procedure/Function Prototypes
  69.  */
  70. #pragma linkage( MainDialogProc, system)
  71. MRESULT EXPENTRY  MainDialogProc( HWND hwnd,
  72.                                   ULONG msg,
  73.                                   MPARAM mp1,
  74.                                   MPARAM mp2);
  75.  
  76. USHORT            DuetMessageBox( USHORT usTitleID,
  77.                                   USHORT usMessageID,
  78.                                   ULONG  ulStyle);
  79.  
  80. VOID              ShowMCIErrorMessage( ULONG ulError);
  81.  
  82. VOID              Initialize( VOID);
  83. VOID              Finalize( VOID);
  84. VOID              InitializeHelp( VOID);
  85. BOOL              PlayTheDuet( HWND hwnd);
  86. BOOL              PauseTheDuet( HWND hwnd);
  87. BOOL              ResumeTheDuet( HWND hwnd);
  88. VOID              StopTheDuet( HWND hwnd);
  89. VOID              CloseTheDuet( VOID);
  90. BOOL              DoesFileExist( PSZ pszFilename);
  91. VOID              SetDuetVolume( HWND hwnd);
  92.  
  93.  
  94. /*************** End of Procedure/Function Prototypes *************************/
  95.  
  96.  
  97. /*
  98.  * Global Variables.
  99.  */
  100. HELPINIT hmiHelpStructure;       /* Help initialization structure.         */
  101.  
  102. HAB      hab;
  103. QMSG     qmsg;
  104. HMQ      hmq;
  105. HWND     hwndMainDialogBox;      /* Handle to the dialog window.           */
  106. HWND     hwndFrame;              /* Handle to the frame window.            */
  107. HWND     hwndHelpInstance;       /* Handle to Help window.                 */
  108.  
  109. enum     DuetStates   eState;
  110. /* state of the selected duet - playing, paused, or stopped */
  111.  
  112. DUET     aDuet[NUM_DUETS]; /* array of duets with information about each   */
  113.  
  114. CHAR     achHelpWindowTitle[LEN_HELP_WINDOW_TITLE];
  115. CHAR     achHelpLibraryName[LEN_HELP_LIBRARY_NAME];
  116.  
  117. USHORT   usDuetPart1ID;              /* device ID for part 1 of the duet      */
  118. USHORT   usDuetPart2ID;              /* device ID for part 2 of the duet      */
  119. USHORT   usGroupHandle;              /* handle to group - duet                */
  120. SHORT    sVolumeLevel;               /* desired volume level                  */
  121.  
  122. BOOL     fPassedDuet      = FALSE;/* for MCI_ACQUIRE to play the Duet         */
  123. BOOL     fSecondDuetPass  = FALSE;/* for setting fPassedDuet-2 devices in grp */
  124. BOOL     fFirstPlay       = TRUE;    /* Indicates we've played for first time */
  125. /************************** End of Global Variables ***************************/
  126.  
  127.  
  128. /******************************************************************************
  129.  * Name         : main
  130.  *
  131.  * Description  : This function calls the Intialize procedure to prepare
  132.  *                everything for the program's operation, enters the
  133.  *                message loop, then call Finalize to shut everything down
  134.  *                when the program is terminated.
  135.  *
  136.  * Concepts     : None.
  137.  *
  138.  * MMPM/2 API's : None.
  139.  *
  140.  * Parameters   : argc - Number of parameters passed into the program.
  141.  *                argv - Command line parameters.
  142.  *
  143.  * Return       : TRUE is returned to the operating system.
  144.  *
  145.  *************************************************************************/
  146. INT main( VOID )
  147. {
  148.  
  149.    Initialize();
  150.  
  151.    while ( WinGetMsg( hab, (PQMSG) &qmsg, (HWND) NULL, 0, 0) )
  152.       WinDispatchMsg( hab, (PQMSG) &qmsg );
  153.  
  154.    Finalize();
  155.  
  156.    return( TRUE);
  157.  
  158. } /* End of main */
  159.  
  160.  
  161.  
  162. /*************************************************************************
  163.  * Name         : Initialize
  164.  *
  165.  * Description  : This function performs the necessary initializations and
  166.  *                setups that are required to show/run a dialog box as a
  167.  *                main window.  The message queue will be created, as will
  168.  *                the dialog box.
  169.  *
  170.  * Concepts     : None.
  171.  *
  172.  * MMPM/2 API's : None.
  173.  *
  174.  * Parameters   : None.
  175.  *
  176.  * Return       : None.
  177.  *
  178.  *************************************************************************/
  179. VOID Initialize( VOID)
  180. {
  181.    CHAR     achTitle[LEN_PROGRAM_TITLE]; /* buffer for window title text      */
  182.    CHAR     szDefaultSize[CCHMAXPATH];   /* buffer for default size menu text */
  183.  
  184.    /*
  185.     * Setup and initialize the dialog window.
  186.     * Change pointer to a waiting pointer first, since this might take a
  187.     * couple of seconds.
  188.     */
  189.  
  190.    /*
  191.     * Setup and initialize the dialog window.
  192.     */
  193.    hab = WinInitialize( 0);
  194.  
  195.    hmq = WinCreateMsgQueue( hab, 0);
  196.  
  197.  
  198.    WinSetPointer(
  199.       HWND_DESKTOP,        /* Desktop window handle.                    */
  200.       WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  201.          HWND_DESKTOP,     /* Desktop window handle.                    */
  202.          SPTR_WAIT,        /* The waiting icon.                         */
  203.          FALSE ) );        /* Return the system pointer's handle.       */
  204.  
  205.    /*
  206.     * Load the strings for the Duet Titles and filenames from the resource
  207.     * file.  This must be done before the dialog box is loaded since it
  208.     * fills the listbox with titles when it receives the WM_INITDLG message.
  209.     */
  210.    WinLoadString(
  211.       hab,
  212.       (HMODULE) NULL,
  213.       IDS_DUET_1_TITLE,
  214.       (SHORT) sizeof( aDuet[0].achTitle),
  215.       aDuet[0].achTitle);
  216.  
  217.    WinLoadString(
  218.       hab,
  219.       (HMODULE) NULL,
  220.       IDS_DUET_1_PART2_FILE,
  221.       (SHORT) sizeof( aDuet[0].achPart2),
  222.       aDuet[0].achPart2);
  223.  
  224.    WinLoadString(
  225.       WinQueryAnchorBlock (HWND_DESKTOP),
  226.       (HMODULE) NULL,
  227.       IDS_DEFAULTSIZE,
  228.       sizeof(szDefaultSize),
  229.       szDefaultSize);
  230.  
  231.    hwndFrame =                  /* Returns the handle to the frame.           */
  232.       WinLoadSecondaryWindow(
  233.          HWND_DESKTOP,          /* Parent of the dialog box.                  */
  234.          HWND_DESKTOP,          /* Owner of the dialog box.                   */
  235.          (PFNWP) MainDialogProc,/* 'Window' procedure for the dialog box.     */
  236.          (HMODULE) NULL,        /* Where is the dialog.  Null is EXE file..   */
  237.          ID_DLG_MAIN,           /* Dialog ID.                                 */
  238.          (PVOID) NULL);         /* Creation Parameters for the dialog.        */
  239.  
  240.    /*
  241.     * Retrieve the handle to the dialog box by specifying the QS_DIALOG flag.
  242.     */
  243.    hwndMainDialogBox = WinQuerySecondaryHWND(hwndFrame, QS_DIALOG);
  244.  
  245.    /*
  246.     * Add Default Size menu item to system menu of the secondary window.
  247.     */
  248.    WinInsertDefaultSize(hwndFrame, szDefaultSize);
  249.  
  250.    /*
  251.     * Get the window title string from the Resource string table
  252.     * and set it as the window text for the dialog window.
  253.     */
  254.    WinLoadString(
  255.       hab,                          /* HAB for this dialog box.            */
  256.       (HMODULE) NULL,               /* Get the string from the .exe file.  */
  257.       IDS_PROGRAM_TITLE,            /* Which string to get.                */
  258.       (SHORT) sizeof( achTitle),    /* The size of the buffer.             */
  259.       achTitle);                    /* The buffer to place the string.     */
  260.  
  261.  
  262.    WinSetWindowText( hwndFrame, achTitle);
  263.  
  264.  
  265.    /*
  266.     * Initialize the help structure and associate the help instance to this
  267.     * dialog via it's handle to anchor block.
  268.     */
  269.    InitializeHelp();
  270.  
  271.    /*
  272.     * Now that we're done here, change the pointer back to the arrow.
  273.     */
  274.  
  275.    WinSetPointer(
  276.       HWND_DESKTOP,        /* Desktop window handle.                    */
  277.       WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  278.          HWND_DESKTOP,     /* Desktop window handle.                    */
  279.          SPTR_ARROW,       /* The Arrow icon.                           */
  280.          FALSE ) );        /* Return the system pointer's handle.       */
  281.  
  282. } /* End of Initialize */
  283.  
  284.  
  285.  
  286. /*************************************************************************
  287.  * Name         : Finalize
  288.  *
  289.  * Description  : This routine is called after the message dispatch loop
  290.  *                has ended because of a WM_QUIT message.  The code will
  291.  *                destroy the help instance, messages queue, and window.
  292.  *
  293.  * Concepts     : None.
  294.  *
  295.  * MMPM/2 API's : None.
  296.  *
  297.  * Parameters   : None.
  298.  *
  299.  * Return       : None.
  300.  *
  301.  *************************************************************************/
  302. VOID Finalize( VOID )
  303. {
  304.    /*
  305.     * Destroy the Help Instance for this dialog window.
  306.     */
  307.    if ( hwndHelpInstance != (HWND) NULL)
  308.    {
  309.       WinDestroyHelpInstance( hwndHelpInstance );
  310.    }
  311.  
  312.    WinDestroySecondaryWindow( hwndFrame );
  313.    WinDestroyMsgQueue( hmq );
  314.    WinTerminate( hab );
  315.  
  316. }  /* End of Finalize */
  317.  
  318.  
  319.  
  320. /******************************************************************************
  321.  * Name         : MainDialogProc
  322.  *
  323.  * Description  : This function controls the main dialog box.  It will
  324.  *                handle received messages such as pushbutton notifications,
  325.  *                timing events, etc.
  326.  *
  327.  * Concepts     : None.
  328.  *
  329.  *
  330.  * MMPM/2 API's : None.
  331.  *
  332.  *
  333.  * Parameters   : hwnd - Handle for the Main dialog box.
  334.  *                msg  - Message received by the dialog box.
  335.  *                mp1  - Parameter 1 for the just recieved message.
  336.  *                mp2  - Parameter 2 for the just recieved message.
  337.  *
  338.  * Return       :
  339.  *
  340.  ******************************************************************************/
  341. MRESULT EXPENTRY MainDialogProc( HWND   hwnd,
  342.                                  ULONG  msg,
  343.                                  MPARAM mp1,
  344.                                  MPARAM mp2 )
  345. {
  346.    HPOINTER          hpProgramIcon;           /* handle to program's icon     */
  347.    PSWP              pswpWindowActionMP;      /* hold status of window        */
  348.    int               iDuet;                   /* index of duet number         */
  349.    USHORT            usUserParm;              /* user parameter returned      */
  350.    USHORT            usNotifyCode;            /* notification message code    */
  351.    USHORT            usCommandMessage;        /* command message for notify   */
  352.    MCI_GENERIC_PARMS mciGenericParms;         /* generic parms for MCI_ACQUIRE*/
  353.    ULONG             ulError;                 /* rc for MCI_ACQUIRE send cmd  */
  354.    CHAR              achAudioHelpFile[LEN_AUDIO_FILENAME];/* Audio file name  */
  355.  
  356.    switch( msg )
  357.    {
  358.  
  359.       case WM_INITDLG :
  360.  
  361.          /*
  362.           * Initialize the dialog window.
  363.           * Change pointer to a waiting pointer first, since this might take a
  364.           * couple of seconds.
  365.           */
  366.  
  367.          WinSetPointer(
  368.             HWND_DESKTOP,        /* Desktop window handle.                    */
  369.             WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  370.                HWND_DESKTOP,     /* Desktop window handle.                    */
  371.                SPTR_WAIT,        /* The waiting icon.                         */
  372.                FALSE ) );        /* Return the system pointer's handle.       */
  373.  
  374.          hpProgramIcon =
  375.             WinLoadPointer(
  376.                HWND_DESKTOP,
  377.                (HMODULE) NULL, /* Where the resource is kept. (Exe file)      */
  378.                ID_ICON );      /* Which icon to use.                          */
  379.  
  380.          WinDefSecondaryWindowProc(
  381.             hwnd,              /* Dialog window handle.                       */
  382.             WM_SETICON,        /* Message to the dialog.  Set it's icon.      */
  383.             (MPARAM) hpProgramIcon,
  384.             (MPARAM) 0 );      /* mp2 no value.                               */
  385.  
  386.          /*
  387.           * We need to fill the listbox with the titles of the duets.
  388.           * To do this, loop through each duet and send a LM_INSERTITEM
  389.           * message to the listbox with the text of the duet's title.
  390.           */
  391.          for(iDuet=0; iDuet<NUM_DUETS; iDuet++)
  392.          {
  393.             WinSendMsg( WinWindowFromID( hwnd, ID_LB_DUET),
  394.                         LM_INSERTITEM,
  395.                         (MPARAM) LIT_END,
  396.                         aDuet[iDuet].achTitle);
  397.          }
  398.  
  399.  
  400.          /* select the first duet in the listbox by default */
  401.  
  402.          WinSendMsg( WinWindowFromID (hwnd, ID_LB_DUET),
  403.                      LM_SELECTITEM,
  404.                      (MPARAM) 0,
  405.                      (MPARAM) TRUE);
  406.  
  407.          sVolumeLevel = INIT_VOLUME;    /* initialize the desired volume level */
  408.  
  409.          /*
  410.           * The slider control cannot be completely set from the dialog
  411.           * template so some aspects must be set here.  We will set the
  412.           * volume range to 0-100, increment to 1-10, and the initial
  413.           * volume level to 75.
  414.           */
  415.          WinSendMsg( WinWindowFromID (hwnd, ID_SL_VOLUME),
  416.                      CSM_SETRANGE,
  417.                      (MPARAM) 0L,
  418.                      (MPARAM) 100L);
  419.  
  420.          WinSendMsg( WinWindowFromID (hwnd, ID_SL_VOLUME),
  421.                      CSM_SETINCREMENT,
  422.                      (MPARAM) 10L,
  423.                      (MPARAM) 1L);
  424.  
  425.          WinSendMsg( WinWindowFromID (hwnd, ID_SL_VOLUME),
  426.                      CSM_SETVALUE,
  427.                      (MPARAM) sVolumeLevel,
  428.                      (MPARAM) NULL);
  429.  
  430.          /*
  431.           * Set up play graphic button.
  432.           */
  433.          WinSendMsg (
  434.             WinWindowFromID(
  435.                hwnd,                /* Dialog window handle             */
  436.                ID_GPB_PLAY),        /* Id - Play graphic button         */
  437.             GBM_SETANIMATIONRATE,   /* Animation rate control           */
  438.             MPFROMLONG(100L),       /* Update play bitmap every .1 sec  */
  439.             NULL);                  /* Ignore return data               */
  440.  
  441.          eState = ST_CLOSED;        /* The initial duet state is closed */
  442.  
  443.          /*
  444.           * Now that we're done here, change the pointer back to the arrow.
  445.           */
  446.  
  447.          WinSetPointer(
  448.             HWND_DESKTOP,        /* Desktop window handle.                    */
  449.             WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  450.                HWND_DESKTOP,     /* Desktop window handle.                    */
  451.                SPTR_ARROW,       /* The Arrow icon.                           */
  452.                FALSE ) );        /* Return the system pointer's handle.       */
  453.  
  454.          return( (MRESULT) 0);
  455.  
  456.  
  457.       case WM_CLOSE :
  458.  
  459.          /*
  460.           * Clean up the devices and group.
  461.           */
  462.  
  463.          if (!fFirstPlay) {        /* If we've opened the devices */
  464.             CloseTheDuet();
  465.          }
  466.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2));
  467.  
  468.       case WM_HELP :
  469.          /*
  470.           * The dialog window has recieved a request for help from the user,
  471.           * i.e., the Help pushbutton was pressed.  Send the HM_DISPLAY_HELP
  472.           * message to the Help Instance with the IPF resource identifier
  473.           * for the correct HM panel.  This will show the help panel for this
  474.           * sample program.
  475.           */
  476.          WinSendMsg( hwndHelpInstance,
  477.                      HM_DISPLAY_HELP,
  478.                      MPFROMSHORT(1),
  479.                      MPFROMSHORT(HM_RESOURCEID));
  480.          return( (MRESULT) 0);
  481.  
  482.       case WM_COMMAND :
  483.          /*
  484.           * To get which pushbutton was pressed the SHORT1FROMMP macro
  485.           * is used.
  486.           */
  487.          switch (SHORT1FROMMP(mp1))
  488.          {
  489.  
  490.             case ID_GPB_PLAY:     /* user selected "Play"    */
  491.                if ((eState==ST_CLOSED) || (eState==ST_STOPPED))
  492.                {
  493.  
  494.                   if (fPassedDuet)
  495.                   {                       /* If we've passed the device away */
  496.  
  497.                      /* If we don't have control of the device (ie. if we've
  498.                       * passed it) then put up an error message.
  499.                       */
  500.  
  501.                      DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  502.                                      IDS_CANT_PROCESS_MESSAGE,
  503.                                      MB_OK | MB_INFORMATION  | MB_MOVEABLE);
  504.                   }
  505.                   else                   /* If we haven't passed the device */
  506.                   {
  507.                      /* eState will be set in PlayTheDuet */
  508.                      if (PlayTheDuet( hwnd))
  509.                      {
  510.  
  511.                         WinEnableWindow( WinWindowFromID( hwnd, ID_LB_DUET),
  512.                                          FALSE);
  513.                         /*
  514.                          * Start the play button animation
  515.                          */
  516.                         WinSendMsg(
  517.                            WinWindowFromID (
  518.                               hwnd,            /* Dialog window handle      */
  519.                               ID_GPB_PLAY),    /* Id - Play graphic button  */
  520.                            GBM_ANIMATE,        /* Animation control         */
  521.                            MPFROMSHORT(TRUE),  /* Animation flag            */
  522.                            NULL);              /* Ignore return data        */
  523.                      }
  524.                      else
  525.                         eState = ST_STOPPED;
  526.                   }  /* End of if we've passed the device away              */
  527.                }
  528.                else if (eState==ST_PAUSED)
  529.                {
  530.                   if (fPassedDuet)
  531.                   {     /* If we've passed the device away                  */
  532.  
  533.                      /* If we don't have control of the device (ie. if we've
  534.                       * passed it) then put up an error message.
  535.                       */
  536.  
  537.                      DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  538.                                      IDS_CANT_PROCESS_MESSAGE,
  539.                                      MB_OK | MB_INFORMATION  | MB_MOVEABLE);
  540.                   }
  541.                   else  /* If we haven't passed the device away             */
  542.                   {
  543.                      if (ResumeTheDuet(hwnd))
  544.                         eState = ST_PLAYING;
  545.                   }  /* End of if we've passed the device away.             */
  546.                }
  547.                break;
  548.  
  549.             case ID_GPB_PAUSE:    /* user selected "Pause"   */
  550.                if (eState==ST_PLAYING || eState==ST_PAUSED)
  551.                {
  552.                   if (fPassedDuet)
  553.                   {  /* If we've passed the device away...                   */
  554.  
  555.                      /* If we don't have control of the device (ie. if we've
  556.                       * passed it) then put up an error message.
  557.                       */
  558.  
  559.                      DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  560.                                      IDS_CANT_PROCESS_MESSAGE,
  561.                                      MB_OK | MB_INFORMATION  | MB_MOVEABLE);
  562.                   }
  563.                   else  /* If we haven't passed the device away...           */
  564.                   {
  565.  
  566.                      if (eState==ST_PLAYING)
  567.                      {
  568.                         if (PauseTheDuet(hwnd))
  569.                            eState = ST_PAUSED;
  570.                      }
  571.                      else
  572.                      {
  573.                         if (ResumeTheDuet(hwnd))
  574.                            eState = ST_PLAYING;
  575.                      }
  576.                   }  /* End of if we've passed the device away               */
  577.                }
  578.                break;
  579.  
  580.             case ID_GPB_STOP:     /* user selected "Stop"    */
  581.                if (eState==ST_PLAYING || eState==ST_PAUSED)
  582.                {
  583.                   if (fPassedDuet)
  584.                   {      /* If we've passed control of device away           */
  585.  
  586.                      /* If we don't have control of the device (ie. if we've
  587.                       * passed it) then put up an error message.
  588.                       */
  589.  
  590.                      DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  591.                                      IDS_CANT_PROCESS_MESSAGE,
  592.                                      MB_OK | MB_INFORMATION  | MB_MOVEABLE);
  593.                   }
  594.                   else
  595.                   {
  596.                      eState = ST_STOPPED;
  597.                      StopTheDuet(hwnd);
  598.                      WinEnableWindow( WinWindowFromID( hwnd, ID_LB_DUET), TRUE);
  599.                   }
  600.                }
  601.                break;
  602.  
  603.             case ID_PB_CANCEL:   /* user selected "Cancel"  */
  604.                WinSendMsg( hwnd, WM_CLOSE, (MPARAM) NULL, (MPARAM) NULL);
  605.                break;
  606.  
  607.             default:
  608.                break;
  609.  
  610.          }  /* End of Command Switch */
  611.          return( (MRESULT) 0);
  612.  
  613.  
  614.       case WM_CONTROL:
  615.          if (SHORT1FROMMP(mp1)==ID_SL_VOLUME)
  616.          {
  617.             if ((SHORT2FROMMP(mp1)==CSN_CHANGED) ||   /* change volume?    */
  618.                 (SHORT2FROMMP(mp1)==CSN_TRACKING))    /* tracking volume?  */
  619.             {
  620.                sVolumeLevel = SHORT1FROMMP (mp2);
  621.                SetDuetVolume(hwnd);
  622.             }
  623.          }
  624.          return( (MRESULT) 0);
  625.  
  626.       case WM_MINMAXFRAME:
  627.  
  628.          /*
  629.           * Handling this message is required since this program is using
  630.           * a dialog box as the main window.  In PM, the controls in the
  631.           * bottom left corner of the dialog box may overlap the icon when
  632.           * the progrma is minimized.
  633.           *
  634.           * Therefore, if the message action indicates that the program is
  635.           * about to be minimized then we will hide the pushbutton in the
  636.           * lower lefthand corner.  Otherwise, we assume the program is about
  637.           * be restored so we will allow the pushbutton to be shown.
  638.           */
  639.  
  640.          pswpWindowActionMP = (PSWP) LONGFROMMP( mp1 );
  641.  
  642.          if ( pswpWindowActionMP->fl & SWP_MINIMIZE )
  643.             WinShowWindow( WinWindowFromID( hwnd, ID_PB_CANCEL), FALSE);
  644.          else
  645.             WinShowWindow( WinWindowFromID( hwnd, ID_PB_CANCEL), TRUE);
  646.  
  647.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2 ) );
  648.  
  649.       /*
  650.        * The next two messages are handled so that the Duet Player
  651.        * application can participate in device sharing.  Since it opens
  652.        * the devices as shareable devices, other applications can gain
  653.        * control of the devices.  When this happens, we will receive a
  654.        * pass device message.  We keep track of this device passing in
  655.        * the fPassedDuet boolean variable.
  656.        *
  657.        * Be careful, though, because we'll be getting a pass device for
  658.        * each device in the group.  Don't issue the acquire till we've
  659.        * gotten the pass device for both devices in the group.  This is
  660.        * kept track of by the fSecondDuetPass variable.
  661.        *
  662.        * If we do not have access to the device when we receive an activate
  663.        * message, then we will issue an acquire device command to gain
  664.        * access to the device.
  665.        */
  666.  
  667.       case MM_MCIPASSDEVICE:
  668.          if (SHORT1FROMMP(mp2) == MCI_GAINING_USE) {          /* GAINING USE */
  669.  
  670.             if (fSecondDuetPass) {           /* If this is the 2nd pass msg. */
  671.  
  672.                fPassedDuet = FALSE;          /* Gaining control of device.   */
  673.                fSecondDuetPass = FALSE;      /* Reset BOOL for next test.    */
  674.  
  675.                if (eState == ST_PLAYING) {      /* If the duet was playing   */
  676.                   WinSendMsg(                   /* Start Play button animation*/
  677.                      WinWindowFromID (
  678.                         hwnd,                   /* Dialog window handle       */
  679.                         ID_GPB_PLAY),           /* Id - Play graphic button   */
  680.                      GBM_ANIMATE,               /* Animation control          */
  681.                      MPFROMSHORT(TRUE),         /* Animation flag             */
  682.                      NULL);                     /* Ignore return data         */
  683.                }
  684.  
  685.             }
  686.             else                             /* If this is the 1st pass msg. */
  687.             {
  688.                fSecondDuetPass = TRUE;       /* Set BOOL for next test.      */
  689.             }
  690.  
  691.          } else {                                             /* LOSING USE  */
  692.  
  693.             if (fSecondDuetPass) {           /* If this is the 2nd pass msg. */
  694.  
  695.                fPassedDuet = TRUE;           /* Losing  control of device.   */
  696.                fSecondDuetPass = FALSE;      /* Reset BOOL for next test.    */
  697.  
  698.                if (eState == ST_PLAYING) {      /* If the duet was playing   */
  699.                   WinSendMsg(                   /* Start Play button animation. */
  700.                      WinWindowFromID (
  701.                         hwnd,                   /* Dialog window handle         */
  702.                         ID_GPB_PLAY),           /* Id - Play graphic button     */
  703.                      GBM_ANIMATE,               /* Animation control            */
  704.                      MPFROMSHORT(FALSE),        /* Animation flag               */
  705.                      NULL);                     /* Ignore return data           */
  706.                }
  707.             }
  708.             else                             /* If this is the 1st pass msg. */
  709.             {
  710.                fSecondDuetPass = TRUE;       /* Set BOOL for next test.      */
  711.             }
  712.          }
  713.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2 ) );
  714.  
  715.       case WM_ACTIVATE:
  716.  
  717.       /**************************************************************
  718.        * We use the WM_ACTIVATE message to participate in device
  719.        * sharing.  We first check to see if this is an activate
  720.        * or a deactivate message (indicated by mp1).  Then,
  721.        * we check to see if we've passed control of the duets'
  722.        * devices.  If these conditions are true, then
  723.        * we issue an acquire device command to regain control of
  724.        * the device, since we're now the active window on the screen.
  725.        *
  726.        * This is one possible method that can be used to implement
  727.        * device sharing. For applications that are more complex
  728.        * than this sample program, developers may wish to take
  729.        * advantage of a more robust method of device sharing.
  730.        * This can be done by using the MCI_ACQUIRE_QUEUE flag on
  731.        * the MCI_ACQUIREDEVICE command.  Please refer to the MMPM/2
  732.        * documentation for more information on this flag.
  733.        **************************************************************/
  734.  
  735.          if ((BOOL)mp1 && fPassedDuet == TRUE) {
  736.  
  737.             mciGenericParms.hwndCallback = hwnd;
  738.  
  739.             ulError = mciSendCommand( usGroupHandle,
  740.                                       MCI_ACQUIREDEVICE,
  741.                                       (ULONG)MCI_NOTIFY,
  742.                                       (PVOID) &mciGenericParms,
  743.                                       (USHORT)NULL);
  744.             if (ulError)
  745.             {
  746.                ShowMCIErrorMessage( ulError);
  747.             }
  748.  
  749.          }
  750.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2 ) );
  751.  
  752.       case MM_MCINOTIFY:
  753.          usNotifyCode = (USHORT) SHORT1FROMMP( mp1);
  754.          usUserParm  = (USHORT) SHORT2FROMMP( mp1);
  755.  
  756.          usCommandMessage = (USHORT) SHORT2FROMMP( mp2); /* high-word */
  757.  
  758.          switch (usCommandMessage)
  759.          {
  760.             case MCI_PLAY:
  761.                switch (usNotifyCode)
  762.                {
  763.                   case MCI_NOTIFY_SUCCESSFUL:
  764.                      if (eState != ST_STOPPED && eState != ST_CLOSED)
  765.                      {
  766.                         /*
  767.                         * We will receive the MCI_NOTIFY_SUCCESSFUL message
  768.                         * for each device in the group, so we need to be sure
  769.                         * to only do this action once.  That's why we are
  770.                         * checking the eState and then immediately setting it
  771.                         * to ST_STOPPED.
  772.                         */
  773.                         eState = ST_STOPPED;
  774.                         StopTheDuet(hwnd);
  775.  
  776.                         WinEnableWindow( WinWindowFromID( hwnd, ID_LB_DUET),
  777.                               TRUE);
  778.                      }
  779.                      break;
  780.  
  781.                   case MCI_NOTIFY_SUPERSEDED:
  782.                   case MCI_NOTIFY_ABORTED:
  783.                      /* we don't need to handle these messages. */
  784.                      break;
  785.  
  786.                   default:
  787.                      /*
  788.                       * If the message is none of the above, then it must be
  789.                       * a notification error message.
  790.                       */
  791.                      ShowMCIErrorMessage( usNotifyCode);
  792.  
  793.                      eState = ST_STOPPED;
  794.                      StopTheDuet(hwnd);
  795.                      break;
  796.  
  797.                }
  798.                break;
  799.          }
  800.          return( (MRESULT) 0);
  801.  
  802.       case HM_INFORM:
  803.          /*
  804.           * The user has selected the "Play Audio Help" selection in the
  805.           * IPF help panel.
  806.           *
  807.           * To initiate the playing of audio help, we need to issue mciPlayFile.
  808.           *
  809.           * Note that we assume the HM_INFORM message came from the "Play
  810.           * Audio Help" selection since it is the only :link. with an inform
  811.           * reftype in the .ipf file.  If there were more, we would have to
  812.           * check the resource identifier to determine for which selection
  813.           * this message was generated.
  814.           */
  815.  
  816.          /*
  817.           * Load the name of the audio help file from the resource
  818.           */
  819.          WinLoadString( hab,
  820.                         (HMODULE) NULL,
  821.                         IDS_HELP_WAVEFILE,
  822.                         (SHORT) sizeof( achAudioHelpFile),
  823.                         achAudioHelpFile);
  824.  
  825.          ulError = mciPlayFile( (HWND)NULL,            /* Ignore owner window */
  826.                                 (PSZ)achAudioHelpFile, /* Audio file to play  */
  827.                                 MCI_ASYNC,             /* Flags               */
  828.                                 (PSZ)NULL,             /* Ignore title        */
  829.                                 (HWND)NULL );          /* Ignore viewport wnd */
  830.          if (ulError)
  831.          {
  832.             ShowMCIErrorMessage( ulError);
  833.          }
  834.          return( (MRESULT) 0);
  835.  
  836.       default:
  837.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2));
  838.  
  839.    }  /* End of msg Switch */
  840.  
  841.    return( (MRESULT) FALSE);
  842.  
  843. } /* End of MainDialogProc */
  844.  
  845.  
  846.  
  847. /*************************************************************************
  848.  * Name         : InitializeHelp
  849.  *
  850.  * Description  : This procedure will set up the initial values in the
  851.  *                global help structure.  This help structure will then
  852.  *                be passed on to the Help Manager when the Help Instance
  853.  *                is created.  The handle to the Help Instance will be
  854.  *                kept for later use.
  855.  *
  856.  * Concepts     : None.
  857.  *
  858.  * MMPM/S API's : None.
  859.  *
  860.  * Parameters   : None.
  861.  *
  862.  * Return       : None.
  863.  *
  864.  *************************************************************************/
  865. VOID InitializeHelp( VOID )
  866. {
  867.    BOOL  fHelpCreated = FALSE;
  868.  
  869.  
  870.    /*
  871.     * Load the strings for the Help window title and library name.
  872.     */
  873.  
  874.    WinLoadString(
  875.       hab,
  876.       (HMODULE) NULL,
  877.       IDS_HELP_WINDOW_TITLE,
  878.       (SHORT) sizeof( achHelpWindowTitle),
  879.       achHelpWindowTitle);
  880.  
  881.    WinLoadString(
  882.       hab,
  883.       (HMODULE) NULL,
  884.       IDS_HELP_LIBRARY_NAME,
  885.       (SHORT) sizeof( achHelpLibraryName),
  886.       achHelpLibraryName);
  887.  
  888.  
  889.    /*
  890.     * Get the size of the initialization structure.
  891.     */
  892.    hmiHelpStructure.cb              = sizeof( HELPINIT);
  893.  
  894.    hmiHelpStructure.ulReturnCode    = (ULONG) 0;   /* RC from HM init      */
  895.    hmiHelpStructure.pszTutorialName = (PSZ) NULL;  /* No tutorial program  */
  896.  
  897.    hmiHelpStructure.phtHelpTable    = (PVOID)(0xffff0000 | ID_DUET_HELPTABLE);
  898.  
  899.  
  900.    /*
  901.     * The action bar is not used, so set the following to NULL.
  902.     */
  903.    hmiHelpStructure.hmodAccelActionBarModule = (HMODULE) 0;
  904.    hmiHelpStructure.idAccelTable             = (USHORT) 0;
  905.    hmiHelpStructure.idActionBar              = (USHORT) 0;
  906.  
  907.    /*
  908.     * The Help window title.
  909.     */
  910.    hmiHelpStructure.pszHelpWindowTitle  = achHelpWindowTitle;
  911.  
  912.    /*
  913.     * The Help table is in the executable file.
  914.     */
  915.    hmiHelpStructure.hmodHelpTableModule = (HMODULE) 0;
  916.  
  917.    /*
  918.     * The help panel ID is not displayed.
  919.     */
  920.    hmiHelpStructure.fShowPanelId       = (ULONG) 0;
  921.  
  922.    /*
  923.     * The library that contains the help panels.
  924.     */
  925.    hmiHelpStructure.pszHelpLibraryName  = achHelpLibraryName;
  926.  
  927.    /*
  928.     * Create the Help Instance for IPF.
  929.     * Give IPF the Anchor Block handle and address of the IPF initialization
  930.     * structure, and check that creation of Help was a success.
  931.     */
  932.    hwndHelpInstance = WinCreateHelpInstance(
  933.                          hab,                   /* Anchor Block Handle.    */
  934.                          &hmiHelpStructure );   /* Help Structure.         */
  935.  
  936.    if ( hwndHelpInstance == (HWND) NULL)
  937.    {
  938.       fHelpCreated = FALSE;
  939.    }
  940.    else
  941.    {
  942.       if ( hmiHelpStructure.ulReturnCode)
  943.       {
  944.          WinDestroyHelpInstance( hwndHelpInstance);
  945.          fHelpCreated = FALSE;
  946.       }
  947.       else  /* help creation worked */
  948.       {
  949.          WinAssociateHelpInstance(
  950.             hwndHelpInstance,     /* The handle of the Help Instance.      */
  951.             hwndFrame );          /* Associate to this dialog window.      */
  952.  
  953.             fHelpCreated = TRUE;
  954.       }
  955.    }  /* End of IF checking the creation of the Help Instance. */
  956.  
  957.    /*
  958.     * Associate the Help Instance of the IPF to this dialog window.
  959.     */
  960.    if (!fHelpCreated)
  961.    {
  962.       DuetMessageBox( IDS_DUET_PLAYER_ERROR,    /* ID of the title         */
  963.                       IDS_HELP_CREATION_FAILED, /* ID of the message       */
  964.                       MB_OK | MB_INFORMATION  | MB_MOVEABLE);  /* style    */
  965.    }
  966.  
  967. }  /* End of InitializeHelp */
  968.  
  969.  
  970. /*************************************************************************
  971.  * Name         :  DuetMessageBox
  972.  *
  973.  * Description  :  This procedure will display messages for the application
  974.  *                 based upon string IDs passed in.  The actual text will be
  975.  *                 loaded from the string table in the resource.
  976.  *
  977.  * Concepts     :  None.
  978.  *
  979.  * MMPM/2 API's :  None.
  980.  *
  981.  * Parameters   :  usTitleID   - ID of the title string
  982.  *                 usMessageID - ID of the message string
  983.  *                 ulStyle     - Style of the message box (WinMessageBox)
  984.  *
  985.  * Return       :  TRUE  -  if the operation was initiated without error.
  986.  *                 FALSE -  if an error occurred.
  987.  *
  988.  *************************************************************************/
  989. USHORT DuetMessageBox(  USHORT usTitleID,
  990.                         USHORT usMessageID,
  991.                         ULONG  ulStyle)
  992. {
  993.    CHAR     achTitle[LEN_ERROR_TITLE];
  994.    CHAR     achMessage[LEN_ERROR_MESSAGE];
  995.    USHORT   usResult;
  996.  
  997.  
  998.  
  999.    /*
  1000.     * Get the string from the Resource defined string table and show it
  1001.     * in the message box.
  1002.     */
  1003.    WinLoadString(
  1004.       hab,                          /* HAB for this dialog box.            */
  1005.       (HMODULE) NULL,               /* Get the string from the .exe file.  */
  1006.       usTitleID,                    /* Which string to get.                */
  1007.       (SHORT) sizeof( achTitle),    /* The size of the buffer.             */
  1008.       achTitle );                   /* The buffer to place the string.     */
  1009.  
  1010.    WinLoadString(
  1011.       hab,                          /* HAB for this dialog box.            */
  1012.       (HMODULE) NULL,               /* Get the string from the .exe file.  */
  1013.       usMessageID,                  /* Which string to get.                */
  1014.       (SHORT) sizeof( achMessage),  /* The size of the buffer.             */
  1015.       achMessage );                 /* The buffer to place the string.     */
  1016.  
  1017.  
  1018.    usResult =
  1019.       WinMessageBox(
  1020.          HWND_DESKTOP,              /* Parent handle of the message box.   */
  1021.          hwndMainDialogBox,         /* Owner handle of the message box.    */
  1022.          achMessage,                /* String to show in the message box.  */
  1023.          achTitle,                  /* Title to shown in the message box.  */
  1024.          (USHORT) ID_MESSAGEBOX,    /* Message Box Id.                     */
  1025.          ulStyle );                 /* The style of the message box.       */
  1026.  
  1027.    return( usResult);
  1028.  
  1029. }  /* End of DuetMessageBox */
  1030.  
  1031.  
  1032.  
  1033. /*************************************************************************
  1034.  * Name         :  PlayTheDuet
  1035.  *
  1036.  * Description  :  This procedure will begin the playing of the duet.
  1037.  *
  1038.  * Concepts     :  Opening mci devices.
  1039.  *                 Creating a group.
  1040.  *                 Playing a group.
  1041.  *
  1042.  * MMPM/2 API's :  mciSendCommand    MCI_OPEN
  1043.  *                                   MCI_PLAY
  1044.  *                                   MCI_CUE
  1045.  *                                   MCI_STATUS
  1046.  *                                   MCI_LOAD
  1047.  *                                   MCI_GROUP
  1048.  *
  1049.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1050.  *
  1051.  * Return       :  TRUE  -  if the operation was initiated without error.
  1052.  *                 FALSE -  if an error occurred.
  1053.  *
  1054.  * IMPORTANT NOTE ABOUT DEVICE SHARING UNDER MMPM/2:
  1055.  *
  1056.  * To be a well behaved MMPM/2 application, you should participate in
  1057.  * the system's device sharing scheme.  This is done by processing the
  1058.  * MM_MCIPASSDEVICE message and the WM_ACTIVATE message in the window
  1059.  * procedure of your application.  When you open the device, you specify
  1060.  * the handle of your window procedure as the callback window handle of
  1061.  * the MCI_OPEN_PARMS data structure.  This handle tells MMPM/2 what
  1062.  * window procedure to send the MM_MCIPASSDEVICE message to when control
  1063.  * of the device you're opening is gained or lost by your app. Also, when
  1064.  * you open the device, you specify if you want exclusive use of the device,
  1065.  * or if you're willing to share the device.  The Duet Player opens all of
  1066.  * its devices as shareable devices.  The MMPM/2 default is to open the
  1067.  * device exclusively unless otherwise specified by the app.
  1068.  *
  1069.  * The implementation shown in the Duet Player Sample Program is just
  1070.  * one possible way to participate in MMPM/2's device sharing scheme.
  1071.  * This implementation was kept simple for the purposes of illustration.
  1072.  * More robust implementations are possible.
  1073.  *
  1074.  * For example, a more complex and robust way of implementing this device
  1075.  * sharing scheme may include logic to make sure we don't lose the device
  1076.  * between the time we open it and the time we play it.  Also, logic could
  1077.  * be added to make sure that another application doesn't have exclusive
  1078.  * use of the device when we open it.  The current implementation in the
  1079.  * Duet Player will simply display an error message to the user when one
  1080.  * of these two situations exist.
  1081.  *
  1082.  *************************************************************************/
  1083. BOOL PlayTheDuet( HWND hwnd)
  1084. {
  1085.    ULONG             ulError;                /* error value from mci calls    */
  1086.    SHORT             sDuet;                  /* duet number to play           */
  1087.    BOOL              bReturn = FALSE;        /* function return value         */
  1088.    MCI_OPEN_PARMS    mopDuetPart;            /* open parms for MCI_OPEN       */
  1089.    MCI_PLAY_PARMS    mppGroup;               /* play parms for MCI_PLAY       */
  1090.    ULONG             ulDeviceList[NUM_PARTS];/* array of device IDs to group  */
  1091.    MCI_GENERIC_PARMS mciGenericParms;        /* generic parms for MCI_CLOSE   */
  1092.    MCI_STATUS_PARMS  mspCD;                  /* for checking status of CD     */
  1093.    MCI_LOAD_PARMS    mlpLoad;                /* load parms for MCI_LOAD       */
  1094.    MCI_GROUP_PARMS   mgpGroupParms;          /* Group Parms for MCI_GROUP     */
  1095.  
  1096.    /* Get the duet selection from the listbox */
  1097.  
  1098.    sDuet = (USHORT) WinSendMsg( WinWindowFromID( hwnd, ID_LB_DUET),
  1099.                                 LM_QUERYSELECTION,
  1100.                                 (MPARAM) LIT_FIRST,
  1101.                                 (MPARAM) NULL);
  1102.  
  1103.    /* this is a safety net - one should always be selected  */
  1104.  
  1105.    if (sDuet==(SHORT)LIT_NONE)
  1106.    {
  1107.       DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1108.                       IDS_NO_DUET_SELECTED,
  1109.                       MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1110.  
  1111.       return( bReturn);
  1112.    }
  1113.  
  1114.  
  1115.    /*
  1116.     * Before we get ready to open the audio file, we will check to
  1117.     * see it the file exists.  This is important to do since we
  1118.     * can open a file that does not exist to record into it.
  1119.     */
  1120.    if ( DoesFileExist( aDuet[sDuet].achPart2))
  1121.    {
  1122.  
  1123.       /*
  1124.        * Change pointer to a waiting pointer first, since this might take a
  1125.        * couple of seconds.
  1126.        */
  1127.  
  1128.       WinSetPointer(
  1129.          HWND_DESKTOP,        /* Desktop window handle.                    */
  1130.          WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  1131.             HWND_DESKTOP,     /* Desktop window handle.                    */
  1132.             SPTR_WAIT,        /* The waiting icon.                         */
  1133.             FALSE ) );        /* Return the system pointer's handle.       */
  1134.  
  1135.       if (fFirstPlay)
  1136.       /* If this is the first time thru this routine, then we need to open
  1137.        * the devices and make the group.  That's what this IF is for.
  1138.        *
  1139.        * On subsequent calls to this routine, the devices are already open
  1140.        * and the group is already made, so we only need to load the
  1141.        * appropriate files onto the devices.
  1142.        */
  1143.       {
  1144.          /*
  1145.           * Open one part of the duet. The first step is to initialize an
  1146.           * MCI_OPEN_PARMS data structure with the appropriate information,
  1147.           * then issue the MCI_OPEN command with the mciSendCommand function.
  1148.           * We will be using an open with only the device type specified.
  1149.           * This will use whatever disc happens to be in the CD.
  1150.           */
  1151.          mopDuetPart.hwndCallback       = hwnd;         /* For MM_MCIPASSDEVICE */
  1152.          mopDuetPart.usDeviceID       = (USHORT)  NULL; /* this is returned   */
  1153.          mopDuetPart.pszDeviceType    = (PSZ)   MCI_DEVTYPE_CD_AUDIO;
  1154.          mopDuetPart.pszElementName   = (PSZ)   NULL; /* doesn't matter       */
  1155.  
  1156.          ulError = mciSendCommand( (USHORT) 0,
  1157.                                    MCI_OPEN,
  1158.                                    MCI_WAIT | MCI_OPEN_TYPE_ID |
  1159.                                    MCI_OPEN_SHAREABLE,
  1160.                                    (PVOID) &mopDuetPart,
  1161.                                    UP_OPEN);
  1162.  
  1163.          if (!ulError)  /* if we opened part 1 */
  1164.          {
  1165.             usDuetPart1ID = mopDuetPart.usDeviceID;
  1166.  
  1167.             /*
  1168.              * We have opened the CD, now we need to be sure that media is
  1169.              * present in the player.
  1170.              */
  1171.  
  1172.             mspCD.ulItem = MCI_STATUS_MEDIA_PRESENT;
  1173.  
  1174.             ulError = mciSendCommand( usDuetPart1ID,
  1175.                                       MCI_STATUS,
  1176.                                       MCI_WAIT | MCI_STATUS_ITEM,
  1177.                                       (PVOID) &mspCD,
  1178.                                       0);
  1179.             if (!ulError)
  1180.             {
  1181.                ShowMCIErrorMessage( ulError);
  1182.             }
  1183.             else if (mspCD.ulReturn == TRUE) /* if we have a CD */
  1184.             {
  1185.                /*
  1186.                 * We have the CD, now we need to be sure that media is
  1187.                 * playable.
  1188.                 */
  1189.  
  1190.                mspCD.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
  1191.  
  1192.                ulError = mciSendCommand( usDuetPart1ID,
  1193.                                          MCI_STATUS,
  1194.                                          MCI_WAIT | MCI_STATUS_ITEM,
  1195.                                          (PVOID) &mspCD,
  1196.                                          0);
  1197.                if (!ulError)
  1198.                {
  1199.                   ShowMCIErrorMessage( ulError);
  1200.                }
  1201.                else if (mspCD.ulReturn != (USHORT) NULL)  /* if CD is playable  */
  1202.                {
  1203.                   /*
  1204.                    * OK, we have a playable CD.  Now, open the other part
  1205.                    */
  1206.                   mopDuetPart.pszDeviceType    = (PSZ)   NULL;
  1207.                   mopDuetPart.pszElementName   = (PSZ)   aDuet[sDuet].achPart2;
  1208.  
  1209.                   ulError = mciSendCommand( (USHORT) 0,
  1210.                                             MCI_OPEN,
  1211.                                             MCI_WAIT | MCI_OPEN_ELEMENT |
  1212.                                             MCI_OPEN_SHAREABLE | MCI_READONLY,
  1213.                                             (PVOID) &mopDuetPart,
  1214.                                             UP_OPEN);
  1215.  
  1216.                   if (!ulError)  /* if we opened part 2 */
  1217.                   {
  1218.                      usDuetPart2ID = mopDuetPart.usDeviceID;
  1219.  
  1220.                      /*
  1221.                       * So far, so good.  Now we need to create a group.  To do
  1222.                       * this, we need to fill an array with the ID's of the
  1223.                       * already open devices that we want to group.  Then we
  1224.                       * call MCI_GROUP to create the group and return us a
  1225.                       * handle to it.
  1226.                       */
  1227.  
  1228.                      ulDeviceList[0] = (ULONG)usDuetPart1ID;
  1229.                      ulDeviceList[1] = (ULONG)usDuetPart2ID;
  1230.  
  1231.                      mgpGroupParms.hwndCallback = (HWND) NULL;          /* Not needed - we're waiting */
  1232.                      mgpGroupParms.ulNumDevices = NUM_PARTS;            /* Count of devices           */
  1233.                      mgpGroupParms.paulDeviceID = (PULONG)&ulDeviceList;/* Array of devices           */
  1234.                      mgpGroupParms.ulStructLength = sizeof (mgpGroupParms);
  1235.  
  1236.                      ulError = mciSendCommand( (USHORT) 0,
  1237.                                                MCI_GROUP,
  1238.                                                MCI_WAIT | MCI_GROUP_MAKE|
  1239.                                                MCI_NOPIECEMEAL,
  1240.                                                (PVOID) &mgpGroupParms,
  1241.                                                UP_GROUP);
  1242.  
  1243.                      fFirstPlay = FALSE;               /* opened the devices  */
  1244.  
  1245.                      if (ulError!=MCIERR_SUCCESS) /* if we did not made the group */
  1246.                      {
  1247.                         /*
  1248.                          * Change the pointer back to an arrow.
  1249.                          */
  1250.  
  1251.                         WinSetPointer(
  1252.                            HWND_DESKTOP,       /* Desktop window handle.             */
  1253.                            WinQuerySysPointer( /* This API gives handle of the PTR.  */
  1254.                               HWND_DESKTOP,    /* Desktop window handle.             */
  1255.                               SPTR_ARROW,      /* The Arrow icon.                    */
  1256.                               FALSE ) );       /* Return the sys pointer's handle.   */
  1257.  
  1258.                         DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1259.                                         IDS_CANNOT_MAKE_GROUP,
  1260.                                         MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1261.  
  1262.                         /* we need to close parts 1 and 2 */
  1263.  
  1264.                         mciSendCommand( usDuetPart1ID,
  1265.                                         MCI_CLOSE,
  1266.                                         MCI_WAIT,
  1267.                                         (PVOID) &mciGenericParms,
  1268.                                         UP_CLOSE);
  1269.  
  1270.                         usDuetPart1ID = (USHORT) NULL;
  1271.  
  1272.                         mciSendCommand( usDuetPart2ID,
  1273.                                         MCI_CLOSE,
  1274.                                         MCI_WAIT,
  1275.                                         (PVOID) &mciGenericParms,
  1276.                                         UP_CLOSE);
  1277.  
  1278.                         usDuetPart2ID = (USHORT) NULL;
  1279.                      }
  1280.                      else  /* We did make the group */
  1281.                      {
  1282.                         /* The handle of the group is returned in the usGroupID
  1283.                          * field of the structure.  Here, assign it to our global
  1284.                          * variable.
  1285.                          */
  1286.                         usGroupHandle = mgpGroupParms.usGroupID;
  1287.                      }
  1288.                   }
  1289.                   else  /* we didn't open part 2 */
  1290.                   {
  1291.  
  1292.                      /*
  1293.                       * Change the pointer back to an arrow.
  1294.                       */
  1295.  
  1296.                      WinSetPointer(
  1297.                      HWND_DESKTOP,        /* Desktop window handle.             */
  1298.                      WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1299.                         HWND_DESKTOP,     /* Desktop window handle.             */
  1300.                         SPTR_ARROW,       /* The Arrow icon.                    */
  1301.                         FALSE ) );        /* Return the sys pointer's handle.   */
  1302.  
  1303.                      ShowMCIErrorMessage( ulError);
  1304.  
  1305.                      /* we need to close part 1 */
  1306.                      mciSendCommand( usDuetPart1ID,
  1307.                                      MCI_CLOSE,
  1308.                                      MCI_WAIT,
  1309.                                      (PVOID) &mciGenericParms,
  1310.                                      UP_CLOSE);
  1311.  
  1312.                      usDuetPart1ID = (USHORT) NULL;
  1313.                   }
  1314.                }
  1315.                else  /* media is not readable */
  1316.                {
  1317.                   DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1318.                                   IDS_DISC_IS_NOT_READABLE,
  1319.                                   MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1320.  
  1321.  
  1322.                   /* we need to close part 1 */
  1323.                   mciSendCommand( usDuetPart1ID,
  1324.                                   MCI_CLOSE,
  1325.                                   MCI_WAIT,
  1326.                                   (PVOID) &mciGenericParms,
  1327.                                   UP_CLOSE);
  1328.  
  1329.                   usDuetPart1ID = (USHORT) NULL;
  1330.                }
  1331.  
  1332.             }
  1333.             else  /* no media present */
  1334.             {
  1335.                DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1336.                                IDS_NO_DISC_IN_CD,
  1337.                                MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1338.  
  1339.  
  1340.                /* we need to close part 1 */
  1341.                mciSendCommand( usDuetPart1ID,
  1342.                                MCI_CLOSE,
  1343.                                MCI_WAIT,
  1344.                                (PVOID) &mciGenericParms,
  1345.                                UP_CLOSE);
  1346.  
  1347.                usDuetPart1ID = (USHORT) NULL;
  1348.             }
  1349.          }
  1350.          else  /* we didn't open part 1 */
  1351.          {
  1352.  
  1353.             /*
  1354.              * Change the pointer back to an arrow.
  1355.              */
  1356.  
  1357.             WinSetPointer(
  1358.                HWND_DESKTOP,        /* Desktop window handle.             */
  1359.                WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1360.                   HWND_DESKTOP,     /* Desktop window handle.             */
  1361.                   SPTR_ARROW,       /* The Arrow icon.                    */
  1362.                   FALSE ) );        /* Return the sys pointer's handle.   */
  1363.  
  1364.             DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1365.                             IDS_NO_CD_PLAYER,
  1366.                             MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1367.          }
  1368.       }
  1369.       else        /* Just load the files  */
  1370.       /* If this is the first time thru this routine, then we need to open
  1371.        * the devices and make the group.
  1372.        *
  1373.        * On subsequent calls to this routine, the devices are already open
  1374.        * and the group is already made, so we only need to load the
  1375.        * appropriate files onto the devices.  That's what this ELSE is for.
  1376.        */
  1377.       {
  1378.          /*
  1379.           * We need to be sure that media is still present in the player.
  1380.           */
  1381.           mspCD.ulItem = MCI_STATUS_MEDIA_PRESENT;
  1382.  
  1383.           ulError = mciSendCommand( usDuetPart1ID,
  1384.                                     MCI_STATUS,
  1385.                                     MCI_WAIT | MCI_STATUS_ITEM,
  1386.                                     (PVOID) &mspCD,
  1387.                                     0);
  1388.           if (!ulError)
  1389.           {
  1390.              ShowMCIErrorMessage( ulError);
  1391.           }
  1392.           if (mspCD.ulReturn == TRUE) /* if we have a CD */
  1393.           {
  1394.  
  1395.              /*
  1396.               * We have the CD, now we need to be sure that media is
  1397.               * playable.
  1398.               */
  1399.  
  1400.              mspCD.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
  1401.  
  1402.              ulError = mciSendCommand( usDuetPart1ID,
  1403.                                        MCI_STATUS,
  1404.                                        MCI_WAIT | MCI_STATUS_ITEM,
  1405.                                        (PVOID) &mspCD,
  1406.                                        0);
  1407.              if (!ulError)
  1408.              {
  1409.                 ShowMCIErrorMessage( ulError);
  1410.              }
  1411.              else if (mspCD.ulReturn != (USHORT) NULL)  /* if CD is playable  */
  1412.              {
  1413.                 /*
  1414.                  * OK, we have a CD. Load the other part of the duet. The first
  1415.                  * step is to initialize an MCI_LOAD_PARMS data structure with
  1416.                  * the appropriate information, then issue the MCI_LOAD command
  1417.                  * with the mciSendCommand function.
  1418.                  */
  1419.                 mlpLoad.hwndCallback       = (HWND) NULL;
  1420.                 mlpLoad.pszElementName   = (PSZ)   aDuet[sDuet].achPart2;
  1421.  
  1422.                 ulError = mciSendCommand( usDuetPart2ID,
  1423.                                           MCI_LOAD,
  1424.                                           MCI_WAIT | MCI_READONLY,
  1425.                                           (PVOID) &mlpLoad,
  1426.                                           (ULONG) NULL);
  1427.                 if (ulError)  /* if we could not loaded part 2 of the duet */
  1428.                 {
  1429.                    /*
  1430.                     * Change the pointer back to an arrow.
  1431.                     */
  1432.  
  1433.                    WinSetPointer(
  1434.                       HWND_DESKTOP,        /* Desktop window handle.             */
  1435.                       WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1436.                          HWND_DESKTOP,     /* Desktop window handle.             */
  1437.                          SPTR_ARROW,       /* The Arrow icon.                    */
  1438.                          FALSE ) );        /* Return the sys pointer's handle.   */
  1439.                    ShowMCIErrorMessage (ulError);
  1440.                 }
  1441.              }
  1442.              else  /* media is not readable */
  1443.              {
  1444.                 DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1445.                                 IDS_DISC_IS_NOT_READABLE,
  1446.                                 MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1447.              }
  1448.           }
  1449.           else  /* no media present */
  1450.           {
  1451.  
  1452.              DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1453.                              IDS_NO_DISC_IN_CD,
  1454.                              MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1455.           }
  1456.       }
  1457.  
  1458.       if (ulError==MCIERR_SUCCESS)
  1459.       {
  1460.           eState = ST_PLAYING;           /* set state to PLAYING    */
  1461.           SetDuetVolume(hwnd);           /* set the starting volume */
  1462.  
  1463.           /*
  1464.            * OK, we've made the group - now we need to start it playing.
  1465.            * First, preroll the devices so they will both start playing
  1466.            * at precisely the same time without any delay.
  1467.            */
  1468.           mciSendCommand( usDuetPart1ID,              /* ID of device   */
  1469.                           MCI_CUE,                    /* CUE message    */
  1470.                           MCI_WAIT | MCI_CUE_OUTPUT,  /* standard flags */
  1471.                           (PVOID)&mciGenericParms,    /* generic struc  */
  1472.                           (USHORT)NULL);              /* no user parm   */
  1473.  
  1474.           mciSendCommand( usDuetPart2ID,              /* ID of device   */
  1475.                           MCI_CUE,                    /* CUE message    */
  1476.                           MCI_WAIT | MCI_CUE_OUTPUT,  /* standard flags */
  1477.                           (PVOID)&mciGenericParms,    /* generic struc  */
  1478.                           (USHORT)NULL);              /* no user parm   */
  1479.  
  1480.           /*
  1481.            * OK, now we need to start playing. First we need to initialize
  1482.            * an MCI_PLAY_PARMS structure with the pertinent information then
  1483.            * issue an MCI_PLAY command via mciSendCommand.  This time we will
  1484.            * be using the notification version of the command - a MM_MCINOTIFY
  1485.            * message will be sent to the window specified in hwndCallback when
  1486.            * the operation is completed.  Since we want the whole audio file
  1487.            * to be played, we won't specify flags of MCI_FROM or MCI_TO so we
  1488.            * don't need to fill in these parameters in the structure.
  1489.            */
  1490.  
  1491.            mppGroup.hwndCallback = hwnd;    /* notify our window    */
  1492.  
  1493.            ulError = mciSendCommand( usGroupHandle,
  1494.                                      MCI_PLAY,
  1495.                                      MCI_NOTIFY,
  1496.                                      (PVOID) &mppGroup,
  1497.                                      UP_PLAY);
  1498.            /*
  1499.             * Change the pointer back to an arrow.
  1500.             */
  1501.  
  1502.            WinSetPointer(
  1503.               HWND_DESKTOP,        /* Desktop window handle.             */
  1504.               WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1505.                  HWND_DESKTOP,     /* Desktop window handle.             */
  1506.                  SPTR_ARROW,       /* The Arrow icon.                    */
  1507.                  FALSE ) );        /* Return the sys pointer's handle.   */
  1508.  
  1509.            if (ulError)
  1510.            {
  1511.               ShowMCIErrorMessage( ulError);
  1512.  
  1513.               /*
  1514.                * If we couldn't play - we need to halt audio gracefully
  1515.                * and get to stable, known state.
  1516.                */
  1517.  
  1518.               eState = ST_STOPPED;
  1519.               StopTheDuet(hwnd);
  1520.            }
  1521.            else
  1522.               bReturn = TRUE;
  1523.       }
  1524.  
  1525.    }
  1526.    else  /* cannot find audio file */
  1527.    {
  1528.       DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1529.                       IDS_CANNOT_FIND_AUDIO_FILE,
  1530.                       MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1531.    }
  1532.  
  1533.    return( bReturn);
  1534.  
  1535. }  /* end of PlayTheDuet */
  1536.  
  1537.  
  1538.  
  1539. /*************************************************************************
  1540.  * Name         :  PauseTheDuet
  1541.  *
  1542.  * Description  :  This procedure will pause a duet that is playing.
  1543.  *
  1544.  * Concepts     :  Pausing a group.
  1545.  *
  1546.  * MMPM/2 API's :  mciSendCommand    MCI_PAUSE
  1547.  *
  1548.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1549.  *
  1550.  * Return       :  TRUE  -  if the operation was initiated without error.
  1551.  *                 FALSE -  if an error occurred.
  1552.  *
  1553.  *************************************************************************/
  1554. BOOL PauseTheDuet( HWND hwnd)
  1555. {
  1556.    ULONG                ulError;          /* return value for mci command  */
  1557.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1558.  
  1559.  
  1560.    /*
  1561.     * Stop the play button animation
  1562.     */
  1563.    WinSendMsg (
  1564.       WinWindowFromID(
  1565.          hwnd,              /* Dialog window handle      */
  1566.          ID_GPB_PLAY),      /* Id - Play graphic button  */
  1567.       GBM_ANIMATE,          /* Animation control         */
  1568.       MPFROMSHORT(FALSE),   /* Animation flag            */
  1569.       NULL);                /* Ignore return data        */
  1570.  
  1571.    /*
  1572.     * To pause the duet, we will issue an MCI_PAUSE command via mciSendCommand
  1573.     * using an MCI_GENERIC_PARMS structure.  This pause command is done for
  1574.     * the group handle rather than the individual device IDs.  This will cause
  1575.     * the action to be performed for the each device in the group.
  1576.     */
  1577.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1578.  
  1579.    ulError = mciSendCommand( usGroupHandle,
  1580.                              MCI_PAUSE,
  1581.                              MCI_NOTIFY,
  1582.                              (PVOID) &mciGenericParms,
  1583.                              UP_PAUSE);
  1584.    if (ulError)
  1585.    {
  1586.       ShowMCIErrorMessage( ulError);
  1587.       return( FALSE);
  1588.    }
  1589.    else
  1590.       return( TRUE);
  1591.  
  1592. }  /* end of PauseTheDuet */
  1593.  
  1594.  
  1595.  
  1596. /*************************************************************************
  1597.  * Name         :  ResumeTheDuet
  1598.  *
  1599.  * Description  :  This procedure will resume the playing of the duet that
  1600.  *                 has been paused.
  1601.  *
  1602.  * Concepts     :  Resuming a group.
  1603.  *
  1604.  * MMPM/2 API's :  mciSendCommand    MCI_RESUME
  1605.  *
  1606.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1607.  *
  1608.  * Return       :  TRUE if the operation was initiated without error.
  1609.  *                 FALSE if an error occurred.
  1610.  *
  1611.  *************************************************************************/
  1612. BOOL ResumeTheDuet( HWND hwnd)
  1613. {
  1614.    ULONG                ulError;          /* return value for mci command  */
  1615.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1616.  
  1617.    /*
  1618.     * To resume the duet, we will issue an MCI_RESUME command via mciSendCommand
  1619.     * using an MCI_GENERIC_PARMS structure.  This pause command is done for
  1620.     * the group handle rather than the individual device IDs.  This will cause
  1621.     * the action to be performed for the each device in the group.
  1622.     */
  1623.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1624.  
  1625.    ulError = mciSendCommand( usGroupHandle,
  1626.                              MCI_RESUME,
  1627.                              MCI_NOTIFY,
  1628.                              (PVOID) &mciGenericParms,
  1629.                              UP_RESUME);
  1630.    if (ulError)
  1631.    {
  1632.       ShowMCIErrorMessage( ulError);
  1633.       return( FALSE);
  1634.    }
  1635.    else
  1636.    {
  1637.       /*
  1638.        * Start the play button animation
  1639.        */
  1640.       WinSendMsg (
  1641.          WinWindowFromID(
  1642.             hwnd,             /* Dialog window handle      */
  1643.             ID_GPB_PLAY),     /* Id - Play graphic button  */
  1644.          GBM_ANIMATE,         /* Animation control         */
  1645.          MPFROMSHORT(TRUE),   /* Animation flag            */
  1646.          NULL);               /* Ignore return data        */
  1647.       return( TRUE);
  1648.    }
  1649.  
  1650. }  /* End of ResumeTheDuet */
  1651.  
  1652.  
  1653.  
  1654. /*************************************************************************
  1655.  * Name         :  StopTheDuet
  1656.  *
  1657.  * Description  :  This procedure will stop the duet that is playing or
  1658.  *                 paused.
  1659.  *
  1660.  * Concepts     :  Stopping a group.
  1661.  *
  1662.  * MMPM/2 API's :  mciSendCommand    MCI_STOP
  1663.  *
  1664.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1665.  *
  1666.  * Return       :  nothing.
  1667.  *
  1668.  *************************************************************************/
  1669. VOID StopTheDuet( HWND hwnd)
  1670. {
  1671.    ULONG                ulError;          /* return value for mci command  */
  1672.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1673.  
  1674.  
  1675.    /*
  1676.     * Stop the play button animation
  1677.     */
  1678.    WinSendMsg (
  1679.       WinWindowFromID(
  1680.          hwnd,             /* Dialog window handle      */
  1681.          ID_GPB_PLAY),     /* Id - Play graphic button  */
  1682.       GBM_ANIMATE,         /* Animation control         */
  1683.       MPFROMSHORT(FALSE),  /* Animation flag            */
  1684.       NULL);               /* Ignore return data        */
  1685.  
  1686.    /*
  1687.     * To stop the duet, we will issue an MCI_STOP command via mciSendCommand
  1688.     * using an MCI_GENERIC_PARMS structure.  This pause command is done for
  1689.     * the group handle rather than the individual device IDs.  This will cause
  1690.     * the action to be performed for the each device in the group.
  1691.     */
  1692.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1693.  
  1694.    ulError = mciSendCommand( usGroupHandle,
  1695.                              MCI_STOP,
  1696.                              MCI_NOTIFY,
  1697.                              (PVOID) &mciGenericParms,
  1698.                              UP_STOP);
  1699.    if (ulError)
  1700.       ShowMCIErrorMessage( ulError);
  1701.  
  1702.    return;
  1703.  
  1704. }  /* end of StopTheDuet */
  1705.  
  1706.  
  1707.  
  1708. /*************************************************************************
  1709.  * Name         :  CloseTheDuet
  1710.  *
  1711.  * Description  :  This procedure will close both parts of the duet and
  1712.  *                 delete the group.
  1713.  *
  1714.  * Concepts     :  Deleting a group.
  1715.  *                 Closing mci devices.
  1716.  *
  1717.  * MMPM/2 API's :  mciSendCommand    MCI_CLOSE
  1718.  *                                   MCI_GROUP
  1719.  *
  1720.  * Parameters   :  None.
  1721.  *
  1722.  * Return       :  nothing.
  1723.  *
  1724.  *************************************************************************/
  1725. VOID CloseTheDuet( VOID)
  1726. {
  1727.    ULONG                ulError;          /* return value for mci command  */
  1728.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1729.    MCI_GROUP_PARMS      mgpGroupParms;    /* Group Parms for MCI_GROUP     */
  1730.  
  1731.  
  1732.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1733.  
  1734.    /*
  1735.     * Now, we will close the group.  This is done by issuing an
  1736.     * MCI_CLOSE command to the groups ID.  Also, we'll set the global
  1737.     * device IDs to NULL since they will no longer be valid.
  1738.     */
  1739.    ulError = mciSendCommand( usGroupHandle,
  1740.                              MCI_CLOSE,
  1741.                              MCI_NOTIFY,
  1742.                              (PVOID) &mciGenericParms,
  1743.                              UP_CLOSE);
  1744.  
  1745.    if (ulError)
  1746.       ShowMCIErrorMessage( ulError);
  1747.  
  1748.    usDuetPart1ID = (USHORT) NULL;
  1749.    usDuetPart2ID = (USHORT) NULL;
  1750.  
  1751.    /* Now, we need to kill the group - also we'll set its handle to NULL.  */
  1752.  
  1753.     mgpGroupParms.hwndCallback = (HWND) NULL;          /* Not needed - we're waiting */
  1754.     mgpGroupParms.usGroupID    = usGroupHandle;        /* Id of group to delete      */
  1755.     mgpGroupParms.ulStructLength = sizeof (mgpGroupParms);
  1756.  
  1757.     ulError = mciSendCommand( (USHORT) 0,
  1758.                                          MCI_GROUP,
  1759.                                          MCI_WAIT | MCI_GROUP_DELETE,
  1760.                                          (PVOID) &mgpGroupParms,
  1761.                                          UP_GROUP);
  1762.    usGroupHandle = (USHORT) NULL;
  1763.  
  1764.    return;
  1765.  
  1766. }  /* end of CloseTheDuet */
  1767.  
  1768.  
  1769. /*************************************************************************
  1770.  * Name         :  ShowMCIErrorMessage
  1771.  *
  1772.  * Description  :  This window procedure displays an MCI error message based
  1773.  *                 based upon a ulError return code.  The MCI function
  1774.  *                 mciGetErrorString is used to convert the error code into
  1775.  *                 a text string and the title is pulled from the resource
  1776.  *                 based upon a string id.
  1777.  *
  1778.  * Concepts     :  Using mciGetErrorString to convert an error code into
  1779.  *                 a textual message.  Error handling of mciGetErrorString.
  1780.  *
  1781.  * MMPM/2 API's :  mciGetErrorString
  1782.  *
  1783.  * Parameters   :  ulError  -  MCI error code.
  1784.  *
  1785.  * Return       :  nothing
  1786.  *
  1787.  *************************************************************************/
  1788. VOID  ShowMCIErrorMessage( ULONG ulError)
  1789. {
  1790.    CHAR  achTitle[LEN_ERROR_TITLE];
  1791.    CHAR  achBuffer[LEN_ERROR_MESSAGE];
  1792.  
  1793.    switch(mciGetErrorString( ulError, (PSZ)achBuffer,   sizeof( achBuffer)))
  1794.    {
  1795.       case MCIERR_SUCCESS:
  1796.          /*
  1797.           * This is what we want.  We were able to use mciGetErrorString to
  1798.           * retrieve a textual error message we can show in a message box.
  1799.           * Now, we need to load the string for the title of the message box.
  1800.           * Then, we'll show it to the user.
  1801.           */
  1802.          WinLoadString( hab,
  1803.                         (HMODULE) NULL,
  1804.                         IDS_DUET_PLAYER_ERROR,
  1805.                         (SHORT) sizeof( achTitle),
  1806.                         achTitle );
  1807.  
  1808.          WinMessageBox( HWND_DESKTOP,
  1809.                         hwndMainDialogBox,
  1810.                         achBuffer,
  1811.                         achTitle,
  1812.                         (USHORT) ID_MESSAGEBOX,
  1813.                         MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1814.          break;
  1815.  
  1816.       case MCIERR_INVALID_DEVICE_ID:
  1817.       case MCIERR_OUTOFRANGE:
  1818.       case MCIERR_INVALID_BUFFER:
  1819.       default:
  1820.          DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1821.                          IDS_UNKNOWN,
  1822.                          MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1823.          break;
  1824.    }
  1825.  
  1826.    return;
  1827.  
  1828. }  /* end of ShowMCIErrorMessage */
  1829.  
  1830.  
  1831. /*************************************************************************
  1832.  * Name         :  DoesFileExist
  1833.  *
  1834.  * Description  :  This helper function determines if a file with a given
  1835.  *                filename exists.
  1836.  *
  1837.  * Concepts     :  Using MMIO interface to access a data file.
  1838.  *
  1839.  * MMPM/2 API's :  mmioOpen
  1840.  *                 mmioClose
  1841.  *
  1842.  * Parameters   :  pszFilename -  The filename to be tested
  1843.  *
  1844.  * Return       :  TRUE  -  if the a file exists matching pszFilename
  1845.  *                 FALSE -  if the file does not exist
  1846.  *
  1847.  *************************************************************************/
  1848. BOOL DoesFileExist( PSZ pszFilename)
  1849. {
  1850.    BOOL  bReturn;    /* function return value   */
  1851.    HMMIO hFile;      /* handle to file          */
  1852.  
  1853.    /*
  1854.     * Notice that these MMIO functions are analogous to the standard
  1855.     * C functions, fopen and fclose.
  1856.     */
  1857.  
  1858.    hFile = mmioOpen( pszFilename, (PMMIOINFO) NULL, MMIO_READ);
  1859.  
  1860.    if (hFile != (HMMIO) NULL)
  1861.    {
  1862.       mmioClose( hFile, 0);
  1863.       bReturn = TRUE;
  1864.    }
  1865.    else
  1866.       bReturn = FALSE;
  1867.  
  1868.    return( bReturn);
  1869. }
  1870.  
  1871.  
  1872.  
  1873. /*************************************************************************
  1874.  * Name         :  SetDuetVolume
  1875.  *
  1876.  * Description  :  This helper function sets the duet volume based upon the
  1877.  *                 position of the volume slider.  The slider will be queried
  1878.  *                 and the duet volume will be set.
  1879.  *
  1880.  * Concepts     :  Setting the volume of a group.
  1881.  *
  1882.  * MMPM/2 API's :  mciSendCommand    MCI_SET
  1883.  *
  1884.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1885.  *
  1886.  * Return       :  nothing.
  1887.  *
  1888.  *************************************************************************/
  1889. VOID SetDuetVolume( HWND hwnd)
  1890. {
  1891.    ULONG                ulError;       /* error value for mci returns   */
  1892.    MCI_WAVE_SET_PARMS   mspSet;        /* set values for volume, etc.   */
  1893.  
  1894.  
  1895.    if ((!fPassedDuet) && (eState==ST_PLAYING || eState==ST_PAUSED))
  1896.    {
  1897.       /*
  1898.        * To set the volume,  first, the MCI_SET_PARMS structure must be
  1899.        * filled with the necessary values.  Then an MCI_SET command
  1900.        * should be issued to each device to perform the volume change.
  1901.        */
  1902.       mspSet.hwndCallback = hwnd;
  1903.       mspSet.ulAudio    = MCI_SET_AUDIO_ALL;    /* set all channels     */
  1904.       mspSet.ulLevel    = (ULONG) sVolumeLevel; /* volume level desired */
  1905.  
  1906.       ulError = mciSendCommand( usDuetPart1ID,
  1907.                                 MCI_SET,
  1908.                                 MCI_NOTIFY | MCI_SET_AUDIO | MCI_SET_VOLUME,
  1909.                                 (PVOID) &mspSet,
  1910.                                 UP_VOLUME);
  1911.  
  1912.       ulError = mciSendCommand( usDuetPart2ID,
  1913.                                 MCI_SET,
  1914.                                 MCI_NOTIFY | MCI_SET_AUDIO | MCI_SET_VOLUME,
  1915.                                 (PVOID) &mspSet,
  1916.                                 UP_VOLUME);
  1917.       if (ulError)
  1918.          ShowMCIErrorMessage( ulError);
  1919.    }
  1920.  
  1921.    return;
  1922.  
  1923. }  /* end of SetDuetVolume */
  1924.