home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tolkit45.zip / os2tk45 / samples / mm / duet1 / duet1.c < prev    next >
C/C++ Source or Header  |  1999-05-11  |  76KB  |  1,969 lines

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