home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / mmpm21tk.zip / TK / DUET2 / DUET2.C next >
C/C++ Source or Header  |  1993-03-02  |  75KB  |  1,916 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.    WinSetPointer(
  191.       HWND_DESKTOP,        /* Desktop window handle.                    */
  192.       WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  193.          HWND_DESKTOP,     /* Desktop window handle.                    */
  194.          SPTR_WAIT,        /* The waiting icon.                         */
  195.          FALSE ) );        /* Return the system pointer's handle.       */
  196.  
  197.    /*
  198.     * Setup and initialize the dialog window.
  199.     */
  200.    hab = WinInitialize( 0);
  201.  
  202.    hmq = WinCreateMsgQueue( hab, 0);
  203.  
  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.                      if (PauseTheDuet(hwnd))
  566.                         eState = ST_PAUSED;
  567.                   }  /* End of if we've passed the device away               */
  568.                }
  569.                break;
  570.  
  571.             case ID_GPB_STOP:     /* user selected "Stop"    */
  572.                if (eState==ST_PLAYING || eState==ST_PAUSED)
  573.                {
  574.                   if (fPassedDuet)
  575.                   {      /* If we've passed control of device away           */
  576.  
  577.                      /* If we don't have control of the device (ie. if we've
  578.                       * passed it) then put up an error message.
  579.                       */
  580.  
  581.                      DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  582.                                      IDS_CANT_PROCESS_MESSAGE,
  583.                                      MB_OK | MB_INFORMATION  | MB_MOVEABLE);
  584.                   }
  585.                   else
  586.                   {
  587.                      eState = ST_STOPPED;
  588.                      StopTheDuet(hwnd);
  589.                      WinEnableWindow( WinWindowFromID( hwnd, ID_LB_DUET), TRUE);
  590.                   }
  591.                }
  592.                break;
  593.  
  594.             case DID_CANCEL:     /* user selected ESC key  */
  595.             case ID_PB_CANCEL:   /* user selected "Cancel"  */
  596.                WinSendMsg( hwnd, WM_CLOSE, (MPARAM) NULL, (MPARAM) NULL);
  597.                break;
  598.  
  599.             default:
  600.                break;
  601.  
  602.          }  /* End of Command Switch */
  603.          return( (MRESULT) 0);
  604.  
  605.  
  606.       case WM_CONTROL:
  607.          if (SHORT1FROMMP(mp1)==ID_SL_VOLUME)
  608.          {
  609.             if ((SHORT2FROMMP(mp1)==CSN_CHANGED) ||   /* change volume?    */
  610.                 (SHORT2FROMMP(mp1)==CSN_TRACKING))    /* tracking volume?  */
  611.             {
  612.                sVolumeLevel = SHORT1FROMMP (mp2);
  613.                SetDuetVolume(hwnd);
  614.             }
  615.          }
  616.          return( (MRESULT) 0);
  617.  
  618.       case WM_MINMAXFRAME:
  619.  
  620.          /*
  621.           * Handling this message is required since this program is using
  622.           * a dialog box as the main window.  In PM, the controls in the
  623.           * bottom left corner of the dialog box may overlap the icon when
  624.           * the progrma is minimized.
  625.           *
  626.           * Therefore, if the message action indicates that the program is
  627.           * about to be minimized then we will hide the pushbutton in the
  628.           * lower lefthand corner.  Otherwise, we assume the program is about
  629.           * be restored so we will allow the pushbutton to be shown.
  630.           */
  631.  
  632.          pswpWindowActionMP = (PSWP) LONGFROMMP( mp1 );
  633.  
  634.          if ( pswpWindowActionMP->fl & SWP_MINIMIZE )
  635.             WinShowWindow( WinWindowFromID( hwnd, ID_PB_CANCEL), FALSE);
  636.          else
  637.             WinShowWindow( WinWindowFromID( hwnd, ID_PB_CANCEL), TRUE);
  638.  
  639.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2 ) );
  640.  
  641.       /*
  642.        * The next two messages are handled so that the Duet Player
  643.        * application can participate in device sharing.  Since it opens
  644.        * the devices as shareable devices, other applications can gain
  645.        * control of the devices.  When this happens, we will receive a
  646.        * pass device message.  We keep track of this device passing in
  647.        * the fPassedDuet boolean variable.
  648.        *
  649.        * Be careful, though, because we'll be getting a pass device for
  650.        * each device in the group.  Don't issue the acquire till we've
  651.        * gotten the pass device for both devices in the group.  This is
  652.        * kept track of by the fSecondDuetPass variable.
  653.        *
  654.        * If we do not have access to the device when we receive an activate
  655.        * message, then we will issue an acquire device command to gain
  656.        * access to the device.
  657.        */
  658.  
  659.       case MM_MCIPASSDEVICE:
  660.          if (SHORT1FROMMP(mp2) == MCI_GAINING_USE) {          /* GAINING USE */
  661.  
  662.             if (fSecondDuetPass) {           /* If this is the 2nd pass msg. */
  663.  
  664.                fPassedDuet = FALSE;          /* Gaining control of device.   */
  665.                fSecondDuetPass = FALSE;      /* Reset BOOL for next test.    */
  666.  
  667.                if (eState == ST_PLAYING) {      /* If the duet was playing   */
  668.                   WinSendMsg(                   /* Start Play button animation*/
  669.                      WinWindowFromID (
  670.                         hwnd,                   /* Dialog window handle       */
  671.                         ID_GPB_PLAY),           /* Id - Play graphic button   */
  672.                      GBM_ANIMATE,               /* Animation control          */
  673.                      MPFROMSHORT(TRUE),         /* Animation flag             */
  674.                      NULL);                     /* Ignore return data         */
  675.                }
  676.  
  677.             }
  678.             else                             /* If this is the 1st pass msg. */
  679.             {
  680.                fSecondDuetPass = TRUE;       /* Set BOOL for next test.      */
  681.             }
  682.  
  683.          } else {                                             /* LOSING USE  */
  684.  
  685.             if (fSecondDuetPass) {           /* If this is the 2nd pass msg. */
  686.  
  687.                fPassedDuet = TRUE;           /* Losing  control of device.   */
  688.                fSecondDuetPass = FALSE;      /* Reset BOOL for next test.    */
  689.  
  690.                if (eState == ST_PLAYING) {      /* If the duet was playing   */
  691.                   WinSendMsg(                   /* Start Play button animation. */
  692.                      WinWindowFromID (
  693.                         hwnd,                   /* Dialog window handle         */
  694.                         ID_GPB_PLAY),           /* Id - Play graphic button     */
  695.                      GBM_ANIMATE,               /* Animation control            */
  696.                      MPFROMSHORT(FALSE),        /* Animation flag               */
  697.                      NULL);                     /* Ignore return data           */
  698.                }
  699.             }
  700.             else                             /* If this is the 1st pass msg. */
  701.             {
  702.                fSecondDuetPass = TRUE;       /* Set BOOL for next test.      */
  703.             }
  704.          }
  705.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2 ) );
  706.  
  707.       case WM_ACTIVATE:
  708.  
  709.       /**************************************************************
  710.        * We use the WM_ACTIVATE message to participate in device
  711.        * sharing.  We first check to see if this is an activate
  712.        * or a deactivate message (indicated by mp1).  Then,
  713.        * we check to see if we've passed control of the duets'
  714.        * devices.  If these conditions are true, then
  715.        * we issue an acquire device command to regain control of
  716.        * the device, since we're now the active window on the screen.
  717.        *
  718.        * This is one possible method that can be used to implement
  719.        * device sharing. For applications that are more complex
  720.        * than this sample program, developers may wish to take
  721.        * advantage of a more robust method of device sharing.
  722.        * This can be done by using the MCI_ACQUIRE_QUEUE flag on
  723.        * the MCI_ACQUIREDEVICE command.  Please refer to the MMPM/2
  724.        * documentation for more information on this flag.
  725.        **************************************************************/
  726.  
  727.          if ((BOOL)mp1 && fPassedDuet == TRUE) {
  728.  
  729.             mciGenericParms.hwndCallback = hwnd;
  730.  
  731.             ulError = mciSendCommand( usGroupHandle,
  732.                                       MCI_ACQUIREDEVICE,
  733.                                       (ULONG)MCI_NOTIFY,
  734.                                       (PVOID) &mciGenericParms,
  735.                                       (USHORT)NULL);
  736.             if (ulError)
  737.             {
  738.                ShowMCIErrorMessage( ulError);
  739.             }
  740.  
  741.          }
  742.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2 ) );
  743.  
  744.       case MM_MCINOTIFY:
  745.          usNotifyCode = (USHORT) SHORT1FROMMP( mp1);
  746.          usUserParm  = (USHORT) SHORT2FROMMP( mp1);
  747.  
  748.          usCommandMessage = (USHORT) SHORT2FROMMP( mp2); /* high-word */
  749.  
  750.          switch (usCommandMessage)
  751.          {
  752.             case MCI_PLAY:
  753.                switch (usNotifyCode)
  754.                {
  755.                   case MCI_NOTIFY_SUCCESSFUL:
  756.                      if (eState != ST_STOPPED && eState != ST_CLOSED)
  757.                      {
  758.                         /*
  759.                         * We will receive the MCI_NOTIFY_SUCCESSFUL message
  760.                         * for each device in the group, so we need to be sure
  761.                         * to only do this action once.  That's why we are
  762.                         * checking the eState and then immediately setting it
  763.                         * to ST_STOPPED.
  764.                         */
  765.                         eState = ST_STOPPED;
  766.                         StopTheDuet(hwnd);
  767.  
  768.                         WinEnableWindow( WinWindowFromID( hwnd, ID_LB_DUET),
  769.                               TRUE);
  770.                      }
  771.                      break;
  772.  
  773.                   case MCI_NOTIFY_SUPERSEDED:
  774.                   case MCI_NOTIFY_ABORTED:
  775.                      /* we don't need to handle these messages. */
  776.                      break;
  777.  
  778.                   default:
  779.                      /*
  780.                       * If the message is none of the above, then it must be
  781.                       * a notification error message.
  782.                       */
  783.                      ShowMCIErrorMessage( usNotifyCode);
  784.  
  785.                      eState = ST_STOPPED;
  786.                      StopTheDuet(hwnd);
  787.                      break;
  788.  
  789.                }
  790.                break;
  791.          }
  792.          return( (MRESULT) 0);
  793.  
  794.       case HM_INFORM:
  795.          /*
  796.           * The user has selected the "Play Audio Help" selection in the
  797.           * IPF help panel.
  798.           *
  799.           * To initiate the playing of audio help, we need to issue mciPlayFile.
  800.           *
  801.           * Note that we assume the HM_INFORM message came from the "Play
  802.           * Audio Help" selection since it is the only :link. with an inform
  803.           * reftype in the .ipf file.  If there were more, we would have to
  804.           * check the resource identifier to determine for which selection
  805.           * this message was generated.
  806.           */
  807.  
  808.          /*
  809.           * Load the name of the audio help file from the resource
  810.           */
  811.          WinLoadString( hab,
  812.                         (HMODULE) NULL,
  813.                         IDS_HELP_WAVEFILE,
  814.                         (SHORT) sizeof( achAudioHelpFile),
  815.                         achAudioHelpFile);
  816.  
  817.          ulError = mciPlayFile( (HWND)NULL,            /* Ignore owner window */
  818.                                 (PSZ)achAudioHelpFile, /* Audio file to play  */
  819.                                 MCI_ASYNC,             /* Flags               */
  820.                                 (PSZ)NULL,             /* Ignore title        */
  821.                                 (HWND)NULL );          /* Ignore viewport wnd */
  822.          if (ulError)
  823.          {
  824.             ShowMCIErrorMessage( ulError);
  825.          }
  826.          return( (MRESULT) 0);
  827.  
  828.       default:
  829.          return( WinDefSecondaryWindowProc( hwnd, msg, mp1, mp2));
  830.  
  831.    }  /* End of msg Switch */
  832.  
  833.    return( (MRESULT) FALSE);
  834.  
  835. } /* End of MainDialogProc */
  836.  
  837.  
  838.  
  839. /*************************************************************************
  840.  * Name         : InitializeHelp
  841.  *
  842.  * Description  : This procedure will set up the initial values in the
  843.  *                global help structure.  This help structure will then
  844.  *                be passed on to the Help Manager when the Help Instance
  845.  *                is created.  The handle to the Help Instance will be
  846.  *                kept for later use.
  847.  *
  848.  * Concepts     : None.
  849.  *
  850.  * MMPM/S API's : None.
  851.  *
  852.  * Parameters   : None.
  853.  *
  854.  * Return       : None.
  855.  *
  856.  *************************************************************************/
  857. VOID InitializeHelp( VOID )
  858. {
  859.    BOOL  fHelpCreated = FALSE;
  860.  
  861.  
  862.    /*
  863.     * Load the strings for the Help window title and library name.
  864.     */
  865.  
  866.    WinLoadString(
  867.       hab,
  868.       (HMODULE) NULL,
  869.       IDS_HELP_WINDOW_TITLE,
  870.       (SHORT) sizeof( achHelpWindowTitle),
  871.       achHelpWindowTitle);
  872.  
  873.    WinLoadString(
  874.       hab,
  875.       (HMODULE) NULL,
  876.       IDS_HELP_LIBRARY_NAME,
  877.       (SHORT) sizeof( achHelpLibraryName),
  878.       achHelpLibraryName);
  879.  
  880.  
  881.    /*
  882.     * Get the size of the initialization structure.
  883.     */
  884.    hmiHelpStructure.cb              = sizeof( HELPINIT);
  885.  
  886.    hmiHelpStructure.ulReturnCode    = (ULONG) 0;   /* RC from HM init      */
  887.    hmiHelpStructure.pszTutorialName = (PSZ) NULL;  /* No tutorial program  */
  888.  
  889.    hmiHelpStructure.phtHelpTable    = (PVOID)(0xffff0000 | ID_DUET_HELPTABLE);
  890.  
  891.  
  892.    /*
  893.     * The action bar is not used, so set the following to NULL.
  894.     */
  895.    hmiHelpStructure.hmodAccelActionBarModule = (HMODULE) 0;
  896.    hmiHelpStructure.idAccelTable             = (USHORT) 0;
  897.    hmiHelpStructure.idActionBar              = (USHORT) 0;
  898.  
  899.    /*
  900.     * The Help window title.
  901.     */
  902.    hmiHelpStructure.pszHelpWindowTitle  = achHelpWindowTitle;
  903.  
  904.    /*
  905.     * The Help table is in the executable file.
  906.     */
  907.    hmiHelpStructure.hmodHelpTableModule = (HMODULE) 0;
  908.  
  909.    /*
  910.     * The help panel ID is not displayed.
  911.     */
  912.    hmiHelpStructure.fShowPanelId       = (ULONG) 0;
  913.  
  914.    /*
  915.     * The library that contains the help panels.
  916.     */
  917.    hmiHelpStructure.pszHelpLibraryName  = achHelpLibraryName;
  918.  
  919.    /*
  920.     * Create the Help Instance for IPF.
  921.     * Give IPF the Anchor Block handle and address of the IPF initialization
  922.     * structure, and check that creation of Help was a success.
  923.     */
  924.    hwndHelpInstance = WinCreateHelpInstance(
  925.                          hab,                   /* Anchor Block Handle.    */
  926.                          &hmiHelpStructure );   /* Help Structure.         */
  927.  
  928.    if ( hwndHelpInstance == (HWND) NULL)
  929.    {
  930.       fHelpCreated = FALSE;
  931.    }
  932.    else
  933.    {
  934.       if ( hmiHelpStructure.ulReturnCode)
  935.       {
  936.          WinDestroyHelpInstance( hwndHelpInstance);
  937.          fHelpCreated = FALSE;
  938.       }
  939.       else  /* help creation worked */
  940.       {
  941.          WinAssociateHelpInstance(
  942.             hwndHelpInstance,     /* The handle of the Help Instance.      */
  943.             hwndFrame );          /* Associate to this dialog window.      */
  944.  
  945.             fHelpCreated = TRUE;
  946.       }
  947.    }  /* End of IF checking the creation of the Help Instance. */
  948.  
  949.    /*
  950.     * Associate the Help Instance of the IPF to this dialog window.
  951.     */
  952.    if (!fHelpCreated)
  953.    {
  954.       DuetMessageBox( IDS_DUET_PLAYER_ERROR,    /* ID of the title         */
  955.                       IDS_HELP_CREATION_FAILED, /* ID of the message       */
  956.                       MB_OK | MB_INFORMATION  | MB_MOVEABLE);  /* style    */
  957.    }
  958.  
  959. }  /* End of InitializeHelp */
  960.  
  961.  
  962. /*************************************************************************
  963.  * Name         :  DuetMessageBox
  964.  *
  965.  * Description  :  This procedure will display messages for the application
  966.  *                 based upon string IDs passed in.  The actual text will be
  967.  *                 loaded from the string table in the resource.
  968.  *
  969.  * Concepts     :  None.
  970.  *
  971.  * MMPM/2 API's :  None.
  972.  *
  973.  * Parameters   :  usTitleID   - ID of the title string
  974.  *                 usMessageID - ID of the message string
  975.  *                 ulStyle     - Style of the message box (WinMessageBox)
  976.  *
  977.  * Return       :  TRUE  -  if the operation was initiated without error.
  978.  *                 FALSE -  if an error occurred.
  979.  *
  980.  *************************************************************************/
  981. USHORT DuetMessageBox(  USHORT usTitleID,
  982.                         USHORT usMessageID,
  983.                         ULONG  ulStyle)
  984. {
  985.    CHAR     achTitle[LEN_ERROR_TITLE];
  986.    CHAR     achMessage[LEN_ERROR_MESSAGE];
  987.    USHORT   usResult;
  988.  
  989.  
  990.  
  991.    /*
  992.     * Get the string from the Resource defined string table and show it
  993.     * in the message box.
  994.     */
  995.    WinLoadString(
  996.       hab,                          /* HAB for this dialog box.            */
  997.       (HMODULE) NULL,               /* Get the string from the .exe file.  */
  998.       usTitleID,                    /* Which string to get.                */
  999.       (SHORT) sizeof( achTitle),    /* The size of the buffer.             */
  1000.       achTitle );                   /* The buffer to place the string.     */
  1001.  
  1002.    WinLoadString(
  1003.       hab,                          /* HAB for this dialog box.            */
  1004.       (HMODULE) NULL,               /* Get the string from the .exe file.  */
  1005.       usMessageID,                  /* Which string to get.                */
  1006.       (SHORT) sizeof( achMessage),  /* The size of the buffer.             */
  1007.       achMessage );                 /* The buffer to place the string.     */
  1008.  
  1009.  
  1010.    usResult =
  1011.       WinMessageBox(
  1012.          HWND_DESKTOP,              /* Parent handle of the message box.   */
  1013.          hwndMainDialogBox,         /* Owner handle of the message box.    */
  1014.          achMessage,                /* String to show in the message box.  */
  1015.          achTitle,                  /* Title to shown in the message box.  */
  1016.          (USHORT) ID_MESSAGEBOX,    /* Message Box Id.                     */
  1017.          ulStyle );                 /* The style of the message box.       */
  1018.  
  1019.    return( usResult);
  1020.  
  1021. }  /* End of DuetMessageBox */
  1022.  
  1023.  
  1024.  
  1025. /*************************************************************************
  1026.  * Name         :  PlayTheDuet
  1027.  *
  1028.  * Description  :  This procedure will begin the playing of the duet.
  1029.  *
  1030.  * Concepts     :  Opening mci devices.
  1031.  *                 Creating a group.
  1032.  *                 Playing a group.
  1033.  *
  1034.  * MMPM/2 API's :  mciSendCommand    MCI_OPEN
  1035.  *                                   MCI_PLAY
  1036.  *                                   MCI_CUE
  1037.  *                                   MCI_STATUS
  1038.  *                                   MCI_LOAD
  1039.  *                                   MCI_GROUP
  1040.  *
  1041.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1042.  *
  1043.  * Return       :  TRUE  -  if the operation was initiated without error.
  1044.  *                 FALSE -  if an error occurred.
  1045.  *
  1046.  * IMPORTANT NOTE ABOUT DEVICE SHARING UNDER MMPM/2:
  1047.  *
  1048.  * To be a well behaved MMPM/2 application, you should participate in
  1049.  * the system's device sharing scheme.  This is done by processing the
  1050.  * MM_MCIPASSDEVICE message and the WM_ACTIVATE message in the window
  1051.  * procedure of your application.  When you open the device, you specify
  1052.  * the handle of your window procedure as the callback window handle of
  1053.  * the MCI_OPEN_PARMS data structure.  This handle tells MMPM/2 what
  1054.  * window procedure to send the MM_MCIPASSDEVICE message to when control
  1055.  * of the device you're opening is gained or lost by your app. Also, when
  1056.  * you open the device, you specify if you want exclusive use of the device,
  1057.  * or if you're willing to share the device.  The Duet Player opens all of
  1058.  * its devices as shareable devices.  The MMPM/2 default is to open the
  1059.  * device exclusively unless otherwise specified by the app.
  1060.  *
  1061.  * The implementation shown in the Duet Player Sample Program is just
  1062.  * one possible way to participate in MMPM/2's device sharing scheme.
  1063.  * This implementation was kept simple for the purposes of illustration.
  1064.  * More robust implementations are possible.
  1065.  *
  1066.  * For example, a more complex and robust way of implementing this device
  1067.  * sharing scheme may include logic to make sure we don't lose the device
  1068.  * between the time we open it and the time we play it.  Also, logic could
  1069.  * be added to make sure that another application doesn't have exclusive
  1070.  * use of the device when we open it.  The current implementation in the
  1071.  * Duet Player will simply display an error message to the user when one
  1072.  * of these two situations exist.
  1073.  *
  1074.  *************************************************************************/
  1075. BOOL PlayTheDuet( HWND hwnd)
  1076. {
  1077.    ULONG             ulError;                /* error value from mci calls    */
  1078.    SHORT             sDuet;                  /* duet number to play           */
  1079.    BOOL              bReturn = FALSE;        /* function return value         */
  1080.    MCI_OPEN_PARMS    mopDuetPart;            /* open parms for MCI_OPEN       */
  1081.    MCI_PLAY_PARMS    mppGroup;               /* play parms for MCI_PLAY       */
  1082.    ULONG             ulDeviceList[NUM_PARTS];/* array of device IDs to group  */
  1083.    MCI_GENERIC_PARMS mciGenericParms;        /* generic parms for MCI_CLOSE   */
  1084.    MCI_STATUS_PARMS  mspCD;                  /* for checking status of CD     */
  1085.    MCI_LOAD_PARMS    mlpLoad;                /* load parms for MCI_LOAD       */
  1086.    MCI_GROUP_PARMS   mgpGroupParms;          /* Group Parms for MCI_GROUP     */
  1087.  
  1088.    /* Get the duet selection from the listbox */
  1089.  
  1090.    sDuet = (USHORT) WinSendMsg( WinWindowFromID( hwnd, ID_LB_DUET),
  1091.                                 LM_QUERYSELECTION,
  1092.                                 (MPARAM) LIT_FIRST,
  1093.                                 (MPARAM) NULL);
  1094.  
  1095.    /* this is a safety net - one should always be selected  */
  1096.  
  1097.    if (sDuet==(SHORT)LIT_NONE)
  1098.    {
  1099.       DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1100.                       IDS_NO_DUET_SELECTED,
  1101.                       MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1102.  
  1103.       return( bReturn);
  1104.    }
  1105.  
  1106.  
  1107.    /*
  1108.     * Before we get ready to open the audio file, we will check to
  1109.     * see it the file exists.  This is important to do since we
  1110.     * can open a file that does not exist to record into it.
  1111.     */
  1112.    if ( DoesFileExist( aDuet[sDuet].achPart2))
  1113.    {
  1114.  
  1115.       /*
  1116.        * Change pointer to a waiting pointer first, since this might take a
  1117.        * couple of seconds.
  1118.        */
  1119.  
  1120.       WinSetPointer(
  1121.          HWND_DESKTOP,        /* Desktop window handle.                    */
  1122.          WinQuerySysPointer(  /* This API will give the handle of the PTR. */
  1123.             HWND_DESKTOP,     /* Desktop window handle.                    */
  1124.             SPTR_WAIT,        /* The waiting icon.                         */
  1125.             FALSE ) );        /* Return the system pointer's handle.       */
  1126.  
  1127.       if (fFirstPlay)
  1128.       /* If this is the first time thru this routine, then we need to open
  1129.        * the devices and make the group.  That's what this IF is for.
  1130.        *
  1131.        * On subsequent calls to this routine, the devices are already open
  1132.        * and the group is already made, so we only need to load the
  1133.        * appropriate files onto the devices.
  1134.        */
  1135.       {
  1136.          /*
  1137.           * Open one part of the duet. The first step is to initialize an
  1138.           * MCI_OPEN_PARMS data structure with the appropriate information,
  1139.           * then issue the MCI_OPEN command with the mciSendCommand function.
  1140.           * We will be using an open with only the device type specified.
  1141.           * This will use whatever disc happens to be in the CD.
  1142.           */
  1143.          mopDuetPart.hwndCallback       = hwnd;         /* For MM_MCIPASSDEVICE */
  1144.          mopDuetPart.usDeviceID       = (USHORT)  NULL; /* this is returned   */
  1145.          mopDuetPart.pszDeviceType    = (PSZ)   MCI_DEVTYPE_CD_AUDIO;
  1146.          mopDuetPart.pszElementName   = (PSZ)   NULL; /* doesn't matter       */
  1147.  
  1148.          ulError = mciSendCommand( (USHORT) 0,
  1149.                                    MCI_OPEN,
  1150.                                    MCI_WAIT | MCI_OPEN_TYPE_ID |
  1151.                                    MCI_OPEN_SHAREABLE,
  1152.                                    (PVOID) &mopDuetPart,
  1153.                                    UP_OPEN);
  1154.  
  1155.          if (!ulError)  /* if we opened part 1 */
  1156.          {
  1157.             usDuetPart1ID = mopDuetPart.usDeviceID;
  1158.  
  1159.             /*
  1160.              * We have opened the CD, now we need to be sure that media is
  1161.              * present in the player.
  1162.              */
  1163.  
  1164.             mspCD.ulItem = MCI_STATUS_MEDIA_PRESENT;
  1165.  
  1166.             ulError = mciSendCommand( usDuetPart1ID,
  1167.                                       MCI_STATUS,
  1168.                                       MCI_WAIT | MCI_STATUS_ITEM,
  1169.                                       (PVOID) &mspCD,
  1170.                                       0);
  1171.             if (!ulError)
  1172.             {
  1173.                ShowMCIErrorMessage( ulError);
  1174.             }
  1175.             else if (mspCD.ulReturn == TRUE) /* if we have a CD */
  1176.             {
  1177.                /*
  1178.                 * We have the CD, now we need to be sure that media is
  1179.                 * playable.
  1180.                 */
  1181.  
  1182.                mspCD.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
  1183.  
  1184.                ulError = mciSendCommand( usDuetPart1ID,
  1185.                                          MCI_STATUS,
  1186.                                          MCI_WAIT | MCI_STATUS_ITEM,
  1187.                                          (PVOID) &mspCD,
  1188.                                          0);
  1189.                if (!ulError)
  1190.                {
  1191.                   ShowMCIErrorMessage( ulError);
  1192.                }
  1193.                else if (mspCD.ulReturn != (USHORT) NULL)  /* if CD is playable  */
  1194.                {
  1195.                   /*
  1196.                    * OK, we have a playable CD.  Now, open the other part
  1197.                    */
  1198.                   mopDuetPart.pszDeviceType    = (PSZ)   NULL;
  1199.                   mopDuetPart.pszElementName   = (PSZ)   aDuet[sDuet].achPart2;
  1200.  
  1201.                   ulError = mciSendCommand( (USHORT) 0,
  1202.                                             MCI_OPEN,
  1203.                                             MCI_WAIT | MCI_OPEN_ELEMENT |
  1204.                                             MCI_OPEN_SHAREABLE | MCI_READONLY,
  1205.                                             (PVOID) &mopDuetPart,
  1206.                                             UP_OPEN);
  1207.  
  1208.                   if (!ulError)  /* if we opened part 2 */
  1209.                   {
  1210.                      usDuetPart2ID = mopDuetPart.usDeviceID;
  1211.  
  1212.                      /*
  1213.                       * So far, so good.  Now we need to create a group.  To do
  1214.                       * this, we need to fill an array with the ID's of the
  1215.                       * already open devices that we want to group.  Then we
  1216.                       * call MCI_GROUP to create the group and return us a
  1217.                       * handle to it.
  1218.                       */
  1219.  
  1220.                      ulDeviceList[0] = (ULONG)usDuetPart1ID;
  1221.                      ulDeviceList[1] = (ULONG)usDuetPart2ID;
  1222.  
  1223.                      mgpGroupParms.hwndCallback = (HWND) NULL;          /* Not needed - we're waiting */
  1224.                      mgpGroupParms.ulNumDevices = NUM_PARTS;            /* Count of devices           */
  1225.                      mgpGroupParms.paulDeviceID = (PULONG)&ulDeviceList;/* Array of devices           */
  1226.                      mgpGroupParms.ulStructLength = sizeof (mgpGroupParms);
  1227.  
  1228.                      ulError = mciSendCommand( (USHORT) 0,
  1229.                                                MCI_GROUP,
  1230.                                                MCI_WAIT | MCI_GROUP_MAKE|
  1231.                                                MCI_NOPIECEMEAL,
  1232.                                                (PVOID) &mgpGroupParms,
  1233.                                                UP_GROUP);
  1234.  
  1235.                      fFirstPlay = FALSE;               /* opened the devices  */
  1236.  
  1237.                      if (ulError!=MCIERR_SUCCESS) /* if we did not made the group */
  1238.                      {
  1239.                         /*
  1240.                          * Change the pointer back to an arrow.
  1241.                          */
  1242.  
  1243.                         WinSetPointer(
  1244.                            HWND_DESKTOP,       /* Desktop window handle.             */
  1245.                            WinQuerySysPointer( /* This API gives handle of the PTR.  */
  1246.                               HWND_DESKTOP,    /* Desktop window handle.             */
  1247.                               SPTR_ARROW,      /* The Arrow icon.                    */
  1248.                               FALSE ) );       /* Return the sys pointer's handle.   */
  1249.  
  1250.                         DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1251.                                         IDS_CANNOT_MAKE_GROUP,
  1252.                                         MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1253.  
  1254.                         /* we need to close parts 1 and 2 */
  1255.  
  1256.                         mciSendCommand( usDuetPart1ID,
  1257.                                         MCI_CLOSE,
  1258.                                         MCI_WAIT,
  1259.                                         (PVOID) &mciGenericParms,
  1260.                                         UP_CLOSE);
  1261.  
  1262.                         usDuetPart1ID = (USHORT) NULL;
  1263.  
  1264.                         mciSendCommand( usDuetPart2ID,
  1265.                                         MCI_CLOSE,
  1266.                                         MCI_WAIT,
  1267.                                         (PVOID) &mciGenericParms,
  1268.                                         UP_CLOSE);
  1269.  
  1270.                         usDuetPart2ID = (USHORT) NULL;
  1271.                      }
  1272.                      else  /* We did make the group */
  1273.                      {
  1274.                         /* The handle of the group is returned in the usGroupID
  1275.                          * field of the structure.  Here, assign it to our global
  1276.                          * variable.
  1277.                          */
  1278.                         usGroupHandle = mgpGroupParms.usGroupID;
  1279.                      }
  1280.                   }
  1281.                   else  /* we didn't open part 2 */
  1282.                   {
  1283.  
  1284.                      /*
  1285.                       * Change the pointer back to an arrow.
  1286.                       */
  1287.  
  1288.                      WinSetPointer(
  1289.                      HWND_DESKTOP,        /* Desktop window handle.             */
  1290.                      WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1291.                         HWND_DESKTOP,     /* Desktop window handle.             */
  1292.                         SPTR_ARROW,       /* The Arrow icon.                    */
  1293.                         FALSE ) );        /* Return the sys pointer's handle.   */
  1294.  
  1295.                      ShowMCIErrorMessage( ulError);
  1296.  
  1297.                      /* we need to close part 1 */
  1298.                      mciSendCommand( usDuetPart1ID,
  1299.                                      MCI_CLOSE,
  1300.                                      MCI_WAIT,
  1301.                                      (PVOID) &mciGenericParms,
  1302.                                      UP_CLOSE);
  1303.  
  1304.                      usDuetPart1ID = (USHORT) NULL;
  1305.                   }
  1306.                }
  1307.                else  /* media is not readable */
  1308.                {
  1309.                   DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1310.                                   IDS_DISC_IS_NOT_READABLE,
  1311.                                   MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1312.  
  1313.  
  1314.                   /* we need to close part 1 */
  1315.                   mciSendCommand( usDuetPart1ID,
  1316.                                   MCI_CLOSE,
  1317.                                   MCI_WAIT,
  1318.                                   (PVOID) &mciGenericParms,
  1319.                                   UP_CLOSE);
  1320.  
  1321.                   usDuetPart1ID = (USHORT) NULL;
  1322.                }
  1323.  
  1324.             }
  1325.             else  /* no media present */
  1326.             {
  1327.                DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1328.                                IDS_NO_DISC_IN_CD,
  1329.                                MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1330.  
  1331.  
  1332.                /* we need to close part 1 */
  1333.                mciSendCommand( usDuetPart1ID,
  1334.                                MCI_CLOSE,
  1335.                                MCI_WAIT,
  1336.                                (PVOID) &mciGenericParms,
  1337.                                UP_CLOSE);
  1338.  
  1339.                usDuetPart1ID = (USHORT) NULL;
  1340.             }
  1341.          }
  1342.          else  /* we didn't open part 1 */
  1343.          {
  1344.  
  1345.             /*
  1346.              * Change the pointer back to an arrow.
  1347.              */
  1348.  
  1349.             WinSetPointer(
  1350.                HWND_DESKTOP,        /* Desktop window handle.             */
  1351.                WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1352.                   HWND_DESKTOP,     /* Desktop window handle.             */
  1353.                   SPTR_ARROW,       /* The Arrow icon.                    */
  1354.                   FALSE ) );        /* Return the sys pointer's handle.   */
  1355.  
  1356.             DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1357.                             IDS_NO_CD_PLAYER,
  1358.                             MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1359.          }
  1360.       }
  1361.       else        /* Just load the files  */
  1362.       /* If this is the first time thru this routine, then we need to open
  1363.        * the devices and make the group.
  1364.        *
  1365.        * On subsequent calls to this routine, the devices are already open
  1366.        * and the group is already made, so we only need to load the
  1367.        * appropriate files onto the devices.  That's what this ELSE is for.
  1368.        */
  1369.       {
  1370.          /*
  1371.           * We need to be sure that media is still present in the player.
  1372.           */
  1373.           mspCD.ulItem = MCI_STATUS_MEDIA_PRESENT;
  1374.  
  1375.           ulError = mciSendCommand( usDuetPart1ID,
  1376.                                     MCI_STATUS,
  1377.                                     MCI_WAIT | MCI_STATUS_ITEM,
  1378.                                     (PVOID) &mspCD,
  1379.                                     0);
  1380.           if (!ulError)
  1381.           {
  1382.              ShowMCIErrorMessage( ulError);
  1383.           }
  1384.           if (mspCD.ulReturn == TRUE) /* if we have a CD */
  1385.           {
  1386.  
  1387.              /*
  1388.               * We have the CD, now we need to be sure that media is
  1389.               * playable.
  1390.               */
  1391.  
  1392.              mspCD.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
  1393.  
  1394.              ulError = mciSendCommand( usDuetPart1ID,
  1395.                                        MCI_STATUS,
  1396.                                        MCI_WAIT | MCI_STATUS_ITEM,
  1397.                                        (PVOID) &mspCD,
  1398.                                        0);
  1399.              if (!ulError)
  1400.              {
  1401.                 ShowMCIErrorMessage( ulError);
  1402.              }
  1403.              else if (mspCD.ulReturn != (USHORT) NULL)  /* if CD is playable  */
  1404.              {
  1405.                 /*
  1406.                  * OK, we have a CD. Load the other part of the duet. The first
  1407.                  * step is to initialize an MCI_LOAD_PARMS data structure with
  1408.                  * the appropriate information, then issue the MCI_LOAD command
  1409.                  * with the mciSendCommand function.
  1410.                  */
  1411.                 mlpLoad.hwndCallback       = (HWND) NULL;
  1412.                 mlpLoad.pszElementName   = (PSZ)   aDuet[sDuet].achPart2;
  1413.  
  1414.                 ulError = mciSendCommand( usDuetPart2ID,
  1415.                                           MCI_LOAD,
  1416.                                           MCI_WAIT | MCI_READONLY,
  1417.                                           (PVOID) &mlpLoad,
  1418.                                           (ULONG) NULL);
  1419.                 if (ulError)  /* if we could not loaded part 2 of the duet */
  1420.                 {
  1421.                    /*
  1422.                     * Change the pointer back to an arrow.
  1423.                     */
  1424.  
  1425.                    WinSetPointer(
  1426.                       HWND_DESKTOP,        /* Desktop window handle.             */
  1427.                       WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1428.                          HWND_DESKTOP,     /* Desktop window handle.             */
  1429.                          SPTR_ARROW,       /* The Arrow icon.                    */
  1430.                          FALSE ) );        /* Return the sys pointer's handle.   */
  1431.                    ShowMCIErrorMessage (ulError);
  1432.                 }
  1433.              }
  1434.              else  /* media is not readable */
  1435.              {
  1436.                 DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1437.                                 IDS_DISC_IS_NOT_READABLE,
  1438.                                 MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1439.              }
  1440.           }
  1441.           else  /* no media present */
  1442.           {
  1443.  
  1444.              DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1445.                              IDS_NO_DISC_IN_CD,
  1446.                              MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1447.           }
  1448.       }
  1449.  
  1450.       if (ulError==MCIERR_SUCCESS)
  1451.       {
  1452.           eState = ST_PLAYING;           /* set state to PLAYING    */
  1453.           SetDuetVolume(hwnd);           /* set the starting volume */
  1454.  
  1455.           /*
  1456.            * OK, we've made the group - now we need to start it playing.
  1457.            * First, preroll the devices so they will both start playing
  1458.            * at precisely the same time without any delay.
  1459.            */
  1460.           mciSendCommand( usDuetPart1ID,              /* ID of device   */
  1461.                           MCI_CUE,                    /* CUE message    */
  1462.                           MCI_WAIT | MCI_CUE_OUTPUT,  /* standard flags */
  1463.                           (PVOID)&mciGenericParms,    /* generic struc  */
  1464.                           (USHORT)NULL);              /* no user parm   */
  1465.  
  1466.           mciSendCommand( usDuetPart2ID,              /* ID of device   */
  1467.                           MCI_CUE,                    /* CUE message    */
  1468.                           MCI_WAIT | MCI_CUE_OUTPUT,  /* standard flags */
  1469.                           (PVOID)&mciGenericParms,    /* generic struc  */
  1470.                           (USHORT)NULL);              /* no user parm   */
  1471.  
  1472.           /*
  1473.            * OK, now we need to start playing. First we need to initialize
  1474.            * an MCI_PLAY_PARMS structure with the pertinent information then
  1475.            * issue an MCI_PLAY command via mciSendCommand.  This time we will
  1476.            * be using the notification version of the command - a MM_MCINOTIFY
  1477.            * message will be sent to the window specified in hwndCallback when
  1478.            * the operation is completed.  Since we want the whole audio file
  1479.            * to be played, we won't specify flags of MCI_FROM or MCI_TO so we
  1480.            * don't need to fill in these parameters in the structure.
  1481.            */
  1482.  
  1483.            mppGroup.hwndCallback = hwnd;    /* notify our window    */
  1484.  
  1485.            ulError = mciSendCommand( usGroupHandle,
  1486.                                      MCI_PLAY,
  1487.                                      MCI_NOTIFY,
  1488.                                      (PVOID) &mppGroup,
  1489.                                      UP_PLAY);
  1490.            /*
  1491.             * Change the pointer back to an arrow.
  1492.             */
  1493.  
  1494.            WinSetPointer(
  1495.               HWND_DESKTOP,        /* Desktop window handle.             */
  1496.               WinQuerySysPointer(  /* This API gives handle of the PTR.  */
  1497.                  HWND_DESKTOP,     /* Desktop window handle.             */
  1498.                  SPTR_ARROW,       /* The Arrow icon.                    */
  1499.                  FALSE ) );        /* Return the sys pointer's handle.   */
  1500.  
  1501.            if (ulError)
  1502.            {
  1503.               ShowMCIErrorMessage( ulError);
  1504.  
  1505.               /*
  1506.                * If we couldn't play - we need to halt audio gracefully
  1507.                * and get to stable, known state.
  1508.                */
  1509.  
  1510.               eState = ST_STOPPED;
  1511.               StopTheDuet(hwnd);
  1512.            }
  1513.            else
  1514.               bReturn = TRUE;
  1515.       }
  1516.  
  1517.    }
  1518.    else  /* cannot find audio file */
  1519.    {
  1520.       DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1521.                       IDS_CANNOT_FIND_AUDIO_FILE,
  1522.                       MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1523.    }
  1524.  
  1525.    return( bReturn);
  1526.  
  1527. }  /* end of PlayTheDuet */
  1528.  
  1529.  
  1530.  
  1531. /*************************************************************************
  1532.  * Name         :  PauseTheDuet
  1533.  *
  1534.  * Description  :  This procedure will pause a duet that is playing.
  1535.  *
  1536.  * Concepts     :  Pausing a group.
  1537.  *
  1538.  * MMPM/2 API's :  mciSendCommand    MCI_PAUSE
  1539.  *
  1540.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1541.  *
  1542.  * Return       :  TRUE  -  if the operation was initiated without error.
  1543.  *                 FALSE -  if an error occurred.
  1544.  *
  1545.  *************************************************************************/
  1546. BOOL PauseTheDuet( HWND hwnd)
  1547. {
  1548.    ULONG                ulError;          /* return value for mci command  */
  1549.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1550.  
  1551.  
  1552.    /*
  1553.     * Stop the play button animation
  1554.     */
  1555.    WinSendMsg (
  1556.       WinWindowFromID(
  1557.          hwnd,              /* Dialog window handle      */
  1558.          ID_GPB_PLAY),      /* Id - Play graphic button  */
  1559.       GBM_ANIMATE,          /* Animation control         */
  1560.       MPFROMSHORT(FALSE),   /* Animation flag            */
  1561.       NULL);                /* Ignore return data        */
  1562.  
  1563.    /*
  1564.     * To pause the duet, we will issue an MCI_PAUSE command via mciSendCommand
  1565.     * using an MCI_GENERIC_PARMS structure.  This pause command is done for
  1566.     * the group handle rather than the individual device IDs.  This will cause
  1567.     * the action to be performed for the each device in the group.
  1568.     */
  1569.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1570.  
  1571.    ulError = mciSendCommand( usGroupHandle,
  1572.                              MCI_PAUSE,
  1573.                              MCI_NOTIFY,
  1574.                              (PVOID) &mciGenericParms,
  1575.                              UP_PAUSE);
  1576.    if (ulError)
  1577.    {
  1578.       ShowMCIErrorMessage( ulError);
  1579.       return( FALSE);
  1580.    }
  1581.    else
  1582.       return( TRUE);
  1583.  
  1584. }  /* end of PauseTheDuet */
  1585.  
  1586.  
  1587.  
  1588. /*************************************************************************
  1589.  * Name         :  ResumeTheDuet
  1590.  *
  1591.  * Description  :  This procedure will resume the playing of the duet that
  1592.  *                 has been paused.
  1593.  *
  1594.  * Concepts     :  Resuming a group.
  1595.  *
  1596.  * MMPM/2 API's :  mciSendCommand    MCI_RESUME
  1597.  *
  1598.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1599.  *
  1600.  * Return       :  TRUE if the operation was initiated without error.
  1601.  *                 FALSE if an error occurred.
  1602.  *
  1603.  *************************************************************************/
  1604. BOOL ResumeTheDuet( HWND hwnd)
  1605. {
  1606.    ULONG                ulError;          /* return value for mci command  */
  1607.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1608.  
  1609.    /*
  1610.     * To resume the duet, we will issue an MCI_RESUME command via mciSendCommand
  1611.     * using an MCI_GENERIC_PARMS structure.  This pause command is done for
  1612.     * the group handle rather than the individual device IDs.  This will cause
  1613.     * the action to be performed for the each device in the group.
  1614.     */
  1615.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1616.  
  1617.    ulError = mciSendCommand( usGroupHandle,
  1618.                              MCI_RESUME,
  1619.                              MCI_NOTIFY,
  1620.                              (PVOID) &mciGenericParms,
  1621.                              UP_RESUME);
  1622.    if (ulError)
  1623.    {
  1624.       ShowMCIErrorMessage( ulError);
  1625.       return( FALSE);
  1626.    }
  1627.    else
  1628.    {
  1629.       /*
  1630.        * Start the play button animation
  1631.        */
  1632.       WinSendMsg (
  1633.          WinWindowFromID(
  1634.             hwnd,             /* Dialog window handle      */
  1635.             ID_GPB_PLAY),     /* Id - Play graphic button  */
  1636.          GBM_ANIMATE,         /* Animation control         */
  1637.          MPFROMSHORT(TRUE),   /* Animation flag            */
  1638.          NULL);               /* Ignore return data        */
  1639.       return( TRUE);
  1640.    }
  1641.  
  1642. }  /* End of ResumeTheDuet */
  1643.  
  1644.  
  1645.  
  1646. /*************************************************************************
  1647.  * Name         :  StopTheDuet
  1648.  *
  1649.  * Description  :  This procedure will stop the duet that is playing or
  1650.  *                 paused.
  1651.  *
  1652.  * Concepts     :  Stopping a group.
  1653.  *
  1654.  * MMPM/2 API's :  mciSendCommand    MCI_STOP
  1655.  *
  1656.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1657.  *
  1658.  * Return       :  nothing.
  1659.  *
  1660.  *************************************************************************/
  1661. VOID StopTheDuet( HWND hwnd)
  1662. {
  1663.    ULONG                ulError;          /* return value for mci command  */
  1664.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1665.  
  1666.  
  1667.    /*
  1668.     * Stop the play button animation
  1669.     */
  1670.    WinSendMsg (
  1671.       WinWindowFromID(
  1672.          hwnd,             /* Dialog window handle      */
  1673.          ID_GPB_PLAY),     /* Id - Play graphic button  */
  1674.       GBM_ANIMATE,         /* Animation control         */
  1675.       MPFROMSHORT(FALSE),  /* Animation flag            */
  1676.       NULL);               /* Ignore return data        */
  1677.  
  1678.    /*
  1679.     * To stop the duet, we will issue an MCI_STOP command via mciSendCommand
  1680.     * using an MCI_GENERIC_PARMS structure.  This pause command is done for
  1681.     * the group handle rather than the individual device IDs.  This will cause
  1682.     * the action to be performed for the each device in the group.
  1683.     */
  1684.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1685.  
  1686.    ulError = mciSendCommand( usGroupHandle,
  1687.                              MCI_STOP,
  1688.                              MCI_NOTIFY,
  1689.                              (PVOID) &mciGenericParms,
  1690.                              UP_STOP);
  1691.    if (ulError)
  1692.       ShowMCIErrorMessage( ulError);
  1693.  
  1694.    return;
  1695.  
  1696. }  /* end of StopTheDuet */
  1697.  
  1698.  
  1699.  
  1700. /*************************************************************************
  1701.  * Name         :  CloseTheDuet
  1702.  *
  1703.  * Description  :  This procedure will close both parts of the duet and
  1704.  *                 delete the group.
  1705.  *
  1706.  * Concepts     :  Deleting a group.
  1707.  *                 Closing mci devices.
  1708.  *
  1709.  * MMPM/2 API's :  mciSendCommand    MCI_CLOSE
  1710.  *                                   MCI_GROUP
  1711.  *
  1712.  * Parameters   :  None.
  1713.  *
  1714.  * Return       :  nothing.
  1715.  *
  1716.  *************************************************************************/
  1717. VOID CloseTheDuet( VOID)
  1718. {
  1719.    ULONG                ulError;          /* return value for mci command  */
  1720.    MCI_GENERIC_PARMS    mciGenericParms;  /* info data structure for cmd.  */
  1721.    MCI_GROUP_PARMS      mgpGroupParms;    /* Group Parms for MCI_GROUP     */
  1722.  
  1723.  
  1724.    mciGenericParms.hwndCallback = hwndMainDialogBox;
  1725.  
  1726.    /*
  1727.     * Now, we will close the group.  This is done by issuing an
  1728.     * MCI_CLOSE command to the groups ID.  Also, we'll set the global
  1729.     * device IDs to NULL since they will no longer be valid.
  1730.     */
  1731.    ulError = mciSendCommand( usGroupHandle,
  1732.                              MCI_CLOSE,
  1733.                              MCI_NOTIFY,
  1734.                              (PVOID) &mciGenericParms,
  1735.                              UP_CLOSE);
  1736.  
  1737.    if (ulError)
  1738.       ShowMCIErrorMessage( ulError);
  1739.  
  1740.    usDuetPart1ID = (USHORT) NULL;
  1741.    usDuetPart2ID = (USHORT) NULL;
  1742.  
  1743.    /* Now, we need to kill the group - also we'll set its handle to NULL.  */
  1744.  
  1745.     mgpGroupParms.hwndCallback = (HWND) NULL;          /* Not needed - we're waiting */
  1746.     mgpGroupParms.usGroupID    = usGroupHandle;        /* Id of group to delete      */
  1747.     mgpGroupParms.ulStructLength = sizeof (mgpGroupParms);
  1748.  
  1749.     ulError = mciSendCommand( (USHORT) 0,
  1750.                                          MCI_GROUP,
  1751.                                          MCI_WAIT | MCI_GROUP_DELETE,
  1752.                                          (PVOID) &mgpGroupParms,
  1753.                                          UP_GROUP);
  1754.    usGroupHandle = (USHORT) NULL;
  1755.  
  1756.    return;
  1757.  
  1758. }  /* end of CloseTheDuet */
  1759.  
  1760.  
  1761. /*************************************************************************
  1762.  * Name         :  ShowMCIErrorMessage
  1763.  *
  1764.  * Description  :  This window procedure displays an MCI error message based
  1765.  *                 based upon a ulError return code.  The MCI function
  1766.  *                 mciGetErrorString is used to convert the error code into
  1767.  *                 a text string and the title is pulled from the resource
  1768.  *                 based upon a string id.
  1769.  *
  1770.  * Concepts     :  Using mciGetErrorString to convert an error code into
  1771.  *                 a textual message.  Error handling of mciGetErrorString.
  1772.  *
  1773.  * MMPM/2 API's :  mciGetErrorString
  1774.  *
  1775.  * Parameters   :  ulError  -  MCI error code.
  1776.  *
  1777.  * Return       :  nothing
  1778.  *
  1779.  *************************************************************************/
  1780. VOID  ShowMCIErrorMessage( ULONG ulError)
  1781. {
  1782.    CHAR  achTitle[LEN_ERROR_TITLE];
  1783.    CHAR  achBuffer[LEN_ERROR_MESSAGE];
  1784.  
  1785.    switch(mciGetErrorString( ulError, (PSZ)achBuffer,   sizeof( achBuffer)))
  1786.    {
  1787.       case MCIERR_SUCCESS:
  1788.          /*
  1789.           * This is what we want.  We were able to use mciGetErrorString to
  1790.           * retrieve a textual error message we can show in a message box.
  1791.           * Now, we need to load the string for the title of the message box.
  1792.           * Then, we'll show it to the user.
  1793.           */
  1794.          WinLoadString( hab,
  1795.                         (HMODULE) NULL,
  1796.                         IDS_DUET_PLAYER_ERROR,
  1797.                         (SHORT) sizeof( achTitle),
  1798.                         achTitle );
  1799.  
  1800.          WinMessageBox( HWND_DESKTOP,
  1801.                         hwndMainDialogBox,
  1802.                         achBuffer,
  1803.                         achTitle,
  1804.                         (USHORT) ID_MESSAGEBOX,
  1805.                         MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1806.          break;
  1807.  
  1808.       case MCIERR_INVALID_DEVICE_ID:
  1809.       case MCIERR_OUTOFRANGE:
  1810.       case MCIERR_INVALID_BUFFER:
  1811.       default:
  1812.          DuetMessageBox( IDS_DUET_PLAYER_ERROR,
  1813.                          IDS_UNKNOWN,
  1814.                          MB_CANCEL | MB_HELP | MB_ERROR | MB_MOVEABLE);
  1815.          break;
  1816.    }
  1817.  
  1818.    return;
  1819.  
  1820. }  /* end of ShowMCIErrorMessage */
  1821.  
  1822.  
  1823. /*************************************************************************
  1824.  * Name         :  DoesFileExist
  1825.  *
  1826.  * Description  :  This helper function determines if a file with a given
  1827.  *                filename exists.
  1828.  *
  1829.  * Concepts     :  Using MMIO interface to access a data file.
  1830.  *
  1831.  * MMPM/2 API's :  mmioOpen
  1832.  *                 mmioClose
  1833.  *
  1834.  * Parameters   :  pszFilename -  The filename to be tested
  1835.  *
  1836.  * Return       :  TRUE  -  if the a file exists matching pszFilename
  1837.  *                 FALSE -  if the file does not exist
  1838.  *
  1839.  *************************************************************************/
  1840. BOOL DoesFileExist( PSZ pszFilename)
  1841. {
  1842.    BOOL  bReturn;    /* function return value   */
  1843.    HMMIO hFile;      /* handle to file          */
  1844.  
  1845.    /*
  1846.     * Notice that these MMIO functions are analogous to the standard
  1847.     * C functions, fopen and fclose.
  1848.     */
  1849.  
  1850.    hFile = mmioOpen( pszFilename, (PMMIOINFO) NULL, MMIO_READ);
  1851.  
  1852.    if (hFile != (HMMIO) NULL)
  1853.    {
  1854.       mmioClose( hFile, 0);
  1855.       bReturn = TRUE;
  1856.    }
  1857.    else
  1858.       bReturn = FALSE;
  1859.  
  1860.    return( bReturn);
  1861. }
  1862.  
  1863.  
  1864.  
  1865. /*************************************************************************
  1866.  * Name         :  SetDuetVolume
  1867.  *
  1868.  * Description  :  This helper function sets the duet volume based upon the
  1869.  *                 position of the volume slider.  The slider will be queried
  1870.  *                 and the duet volume will be set.
  1871.  *
  1872.  * Concepts     :  Setting the volume of a group.
  1873.  *
  1874.  * MMPM/2 API's :  mciSendCommand    MCI_SET
  1875.  *
  1876.  * Parameters   :  hwnd - Handle for the Main dialog box.
  1877.  *
  1878.  * Return       :  nothing.
  1879.  *
  1880.  *************************************************************************/
  1881. VOID SetDuetVolume( HWND hwnd)
  1882. {
  1883.    ULONG                ulError;       /* error value for mci returns   */
  1884.    MCI_WAVE_SET_PARMS   mspSet;        /* set values for volume, etc.   */
  1885.  
  1886.  
  1887.    if ((!fPassedDuet) && (eState==ST_PLAYING || eState==ST_PAUSED))
  1888.    {
  1889.       /*
  1890.        * To set the volume,  first, the MCI_SET_PARMS structure must be
  1891.        * filled with the necessary values.  Then an MCI_SET command
  1892.        * should be issued to each device to perform the volume change.
  1893.        */
  1894.       mspSet.hwndCallback = hwnd;
  1895.       mspSet.ulAudio    = MCI_SET_AUDIO_ALL;    /* set all channels     */
  1896.       mspSet.ulLevel    = (ULONG) sVolumeLevel; /* volume level desired */
  1897.  
  1898.       ulError = mciSendCommand( usDuetPart1ID,
  1899.                                 MCI_SET,
  1900.                                 MCI_NOTIFY | MCI_SET_AUDIO | MCI_SET_VOLUME,
  1901.                                 (PVOID) &mspSet,
  1902.                                 UP_VOLUME);
  1903.  
  1904.       ulError = mciSendCommand( usDuetPart2ID,
  1905.                                 MCI_SET,
  1906.                                 MCI_NOTIFY | MCI_SET_AUDIO | MCI_SET_VOLUME,
  1907.                                 (PVOID) &mspSet,
  1908.                                 UP_VOLUME);
  1909.       if (ulError)
  1910.          ShowMCIErrorMessage( ulError);
  1911.    }
  1912.  
  1913.    return;
  1914.  
  1915. }  /* end of SetDuetVolume */
  1916.