home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / mmpm21tk.zip / TK / ADMCT / ADMCPLAY.C < prev    next >
C/C++ Source or Header  |  1993-04-08  |  49KB  |  1,424 lines

  1. /********************* START OF SPECIFICATIONS *********************
  2. *
  3. * SUBROUTINE NAME: ADMCPLAY.C
  4. *
  5. *
  6. *
  7. *              Copyright (c) IBM Corporation  1991, 1993
  8. *                        All Rights Reserved
  9. *
  10. * DESCRIPTIVE NAME: Audio MCD WaveAudio Playback Source.
  11. *
  12. * FUNCTION:Play an Waveform Audio Element.
  13. *
  14. * On MCI_PLAY, a streaming MCD should perform the following commands:
  15. *
  16. * Always check flags and validate memory first.  This way if the flags
  17. *  are invalid, the previous command will not be interrupted.
  18. * If there is a command active on another thread (i.e. and play, record
  19. *  or save), then either abort (record or save) or superced (play) by
  20. *  stopping the stream and sending a message to the caller.
  21. * If the stream is going the wrong way (e.g. it is setup for recording)
  22. *  then destroy the stream.
  23. * If no stream has been created, then create one.  If the stream handler
  24. *  needs to associate a data object, do it here.
  25. * If we destroyed a recording stream before creating the play back stream,
  26. *  ensure that playback stream has the same position as the previous record
  27. *  stream.
  28. * Enable any events (such as cuepoints or position advises).
  29. * Set up networking hooks,
  30. * Start stream.
  31. * Wait for a streaming event.
  32. * Stop the stream if necessary.
  33. * If MCI_NOTIFY was sent used, inform the caller of command completion.
  34. *
  35. * NOTES: This source file illustrates the following concepts:
  36. *         A. Aborting or superceding a previous command
  37. *         B. Transitioning from a record stream->playback stream
  38. *            so that the caller at the MCI Layer is unaware of the
  39. *            change.
  40. *         C. Processing a stream which was altered by the MCI_SET
  41. *            command.
  42. *         D. Proper creation of a stream.
  43. *         E. Processing the MCI_FROM/MCI_TO flags in a streaming
  44. *            environment.
  45. *         F. Enable both cuepoints and position advises.
  46. *         G. How to use MMIO networking features.
  47. *         H. Determing when the creation of a thread is necessary
  48. *            for an MCI_NOTIFY
  49. *         I. Proper handling of an event procedure.
  50. *         J. Proper termination of a playback thread.
  51. *
  52. * ENTRY POINTS:
  53. *
  54. * INPUT: MCI_PLAY message.
  55. *        MCI_FROM Flag
  56. *        MCI_TO   Flag
  57. *
  58. * EXIT-NORMAL: MCIERR_SUCCESS
  59. *
  60. * EXIT_ERROR:  Error Code.
  61. *
  62. * EFFECTS:
  63. *
  64. *
  65. * INTERNAL REFERENCES:   CreateNAssocStream ().
  66. *                        DestroyStream ().
  67. *                        AssocMemPlayToAudioStream ().
  68. *                        PostMDMMessage ().
  69. *                        CreateToEvent().
  70. *                        ConvertTimeUnits ().
  71. *                        StartPlay().
  72. *                        PlEventProc().
  73. *                        SetAudioDevice().
  74. *                        SetWaveDeviceDefaults().
  75. *                        OpenFile().
  76. *                        CheckMem ().
  77. *
  78. * EXTERNAL REFERENCES:   DosResetEventSem ()        - OS/2 API
  79. *                        DosPostEventSem  ()        - OS/2 API
  80. *                        DosCreateThread  ()        - OS/2 API
  81. *                        SpiEnableEvent   ()        - MME API
  82. *                        SpiStopStream    ()        - MME API
  83. *                        SpiCreateStream  ()        - MME API
  84. *                        SpiAssociate     ()        - MME API
  85. *                        SpiSeekStream    ()        - MME API
  86. *                        mdmDriverNotify  ()        - MME API
  87. *                        mmioGetHeader    ()        - MME API
  88. *
  89. *********************** END OF SPECIFICATIONS **********************/
  90. #define INCL_BASE
  91. #define INCL_DOSMODULEMGR
  92. #define INCL_DOSSEMAPHORES
  93.  
  94. #include <os2.h>
  95. #include <string.h>
  96. #include <os2medef.h>                   // MME includes files.
  97. #include <audio.h>                      // Audio Device defines
  98. #include <ssm.h>                        // SSM spi includes.
  99. #include <meerror.h>                    // MM Error Messages.
  100. #include <mmioos2.h>                    // MMIO Include.
  101. #include <mcios2.h>                     // MM System Include.
  102. #include <mmdrvos2.h>                   // Mci Driver Include.
  103. #include <mcd.h>                        // VSDIDriverInterface.
  104. #include <hhpheap.h>                    // Heap Manager Definitions
  105. #include <qos.h>
  106. #include <audiomcd.h>                   // Component Definitions.
  107. #include "admcfunc.h"                   // Function Prototypes
  108.  
  109.  
  110.  
  111. /********************* START OF SPECIFICATIONS *******************************
  112. *
  113. * SUBROUTINE NAME: CheckPlayFlags
  114. *
  115. * DESCRIPTIVE NAME: Ensures that the play flags are valid.Record
  116. *
  117. * FUNCTION: Report errors, do preprocessing for MCI play
  118. *
  119. * ENTRY POINTS:
  120. *
  121. * NOTE: All MCI Functions should check to ensure that the flags are
  122. *       valid before interrupting a previous operation.  This means
  123. *       that if the caller supplied bad flags while a play was
  124. *       operating, the play would not be interrupted.
  125. *
  126. * EXIT-NORMAL: Return Code 0.
  127. *
  128. * EXIT_ERROR:  Error Code.
  129. *
  130. * EFFECTS:
  131. *
  132. * INTERNAL REFERENCES: WaveIOPROC.
  133. *
  134. * EXTERNAL REFERENCES: DosQueryProcAddr - OS/2 API.
  135. *
  136. *********************** END OF SPECIFICATIONS *******************************/
  137.  
  138. RC CheckPlayFlags ( FUNCTION_PARM_BLOCK *pFuncBlock )
  139.  
  140. {
  141.  
  142.   ULONG                ulrc;                  // RC
  143.   ULONG                ulParam1;              // Incoming MCI Flags
  144.  
  145.   INSTANCE             *ulpInstance;          // Local Instance
  146.  
  147.   ULONG                ulPlayFlags;           // Mask for MCI Play
  148.   ULONG                ulFileLength;          // Length of the File in MMTIME
  149.   ULONG                ulTemp1 = 0;           // Scratch For Time Conversion
  150.   ULONG                ulTempTO = 0;          // Scratch for Play Till
  151.   ULONG                ulFromPosition = 0;    // holds where we will play from
  152.  
  153.   PMCI_PLAY_PARMS     pPlayParms;           // caller data struct
  154.  
  155.  
  156.  
  157.   /* Intialize local variables */
  158.  
  159.   ulParam1 = pFuncBlock->ulParam1;
  160.   ulpInstance = (INSTANCE *) pFuncBlock->ulpInstance;
  161.  
  162.       /*********************************
  163.        Check For Illegal Flags
  164.       **********************************/
  165.  
  166.       ulPlayFlags = pFuncBlock->ulParam1;
  167.       ulPlayFlags &= ~(MCI_FROM + MCI_TO + MCI_NOTIFY + MCI_WAIT);
  168.  
  169.       /* If the caller passed in an  unsupported flag--return an error */
  170.  
  171.       if (ulPlayFlags > 0)
  172.           return ( MCIERR_INVALID_FLAG );
  173.  
  174.       /*******************************************
  175.       * Check to see If A Valid Element Specified
  176.       *******************************************/
  177.  
  178.       if (ulpInstance->fFileExists != TRUE)
  179.         return ( MCIERR_FILE_NOT_FOUND );
  180.  
  181.       /***********************************
  182.       * Retrieve the callers play parms
  183.       ***********************************/
  184.  
  185.       pPlayParms = ( PMCI_PLAY_PARMS )pFuncBlock->ulParam2;
  186.  
  187.  
  188.       /****************************************
  189.       * The caller is required to pass in
  190.       * valid play parms if the MCI_TO, MCI_FROM
  191.       * or if it is a notify.
  192.       *****************************************/
  193.  
  194.       if ((pFuncBlock->ulParam1 & MCI_TO) ||
  195.           (pFuncBlock->ulParam1 & MCI_FROM) ||
  196.           (pFuncBlock->ulNotify))
  197.           {
  198.           ulrc = CheckMem ((PVOID)pPlayParms,
  199.                            sizeof (MCI_PLAY_PARMS), PAG_READ);
  200.  
  201.  
  202.           if (ulrc != MCIERR_SUCCESS)
  203.               return ( MCIERR_MISSING_PARAMETER );
  204.  
  205.           } /* if play parms were passed in */
  206.  
  207.  
  208.       if (ulpInstance->usPlayLstStrm == TRUE)
  209.          {
  210.          return ( MCIERR_SUCCESS );
  211.          }
  212.  
  213.  
  214.       /*********************************************
  215.       * In order to determine if the play request
  216.       * is valid, we must retrieve the length of
  217.       * the file.
  218.       * Note: the length of the file is returned in
  219.       * the time units we are using currently.
  220.       *********************************************/
  221.  
  222.       ConvertTimeUnits ( ulpInstance,
  223.                          &ulFileLength,
  224.                          FILE_LENGTH);
  225.  
  226.       ulTemp1 = ulFileLength;
  227.  
  228.       /* Convert file length to mmtime for stream comparisons later */
  229.  
  230.       ConvertToMM( ulpInstance, &ulFileLength, ulFileLength );
  231.  
  232.  
  233.       /*****************************************************
  234.       * To ensure that the MCI_FROM is valid, we need to
  235.       * do the following checks:
  236.       * If we currently have a playback stream then:
  237.       *   A. Is the from greater than the file length?
  238.       *
  239.       * If a record stream is active then:
  240.       *   A. If it has not been started, then is it
  241.       *      shorter than the file length.
  242.       *   A. Else it must be less than both the file
  243.       *      length AND what we have recorded so far.
  244.       ******************************************************/
  245.  
  246.       if (ulParam1 & MCI_FROM)
  247.          {
  248.          /*******************************************
  249.          * If we have a playback stream and the
  250.          * request is not less than the file length
  251.          * then return an outofrange error.
  252.          *******************************************/
  253.  
  254.          if ( pPlayParms->ulFrom > ulTemp1
  255.               && AMPMIX.ulOperation != OPERATION_RECORD )
  256.             {
  257.             return ( MCIERR_OUTOFRANGE );
  258.             }
  259.          else
  260.             {
  261.             /***************************************
  262.             * This is a record stream, check if
  263.             * a stream been created and started
  264.             * by a play/record or cue?
  265.             * If not, just ensure that the request
  266.             * is within the limits of the file.
  267.             ****************************************/
  268.  
  269.              if ( ulpInstance->ulCreateFlag != PREROLL_STATE )
  270.                 {
  271.                 if ( pPlayParms->ulFrom > ulTemp1 )
  272.                   {
  273.                   return ( MCIERR_OUTOFRANGE );
  274.                   }
  275.  
  276.                 } /* if not in preroll state */
  277.              else
  278.  
  279.                 /*******************************************************
  280.                 * We have a record stream which is active.  This is
  281.                 * special case since record can be continually updating
  282.                 * the file length.  Therefore we must assure that
  283.                 * the request is less than what has been recorded and
  284.                 * the file length.
  285.                 *******************************************************/
  286.  
  287.                 {
  288.  
  289.                 /* Determine where we are in the stream */
  290.  
  291.                 ulrc = SpiGetTime( ulpInstance->StreamInfo.hStream,
  292.                                    ( PMMTIME ) &( ulpInstance->StreamInfo.mmStreamTime ) );
  293.  
  294.                 if ( ulrc )
  295.                   {
  296.                   return ( ulrc );
  297.                   }
  298.  
  299.                 if ( AMPMIX.ulOperation == OPERATION_RECORD )
  300.                    {
  301.  
  302.                    /*******************************************
  303.                    * Convert the from request to mmtime so we
  304.                    * can compare it with the stream time.
  305.                    * The new from time will be place in ulTemp1
  306.                    ********************************************/
  307.  
  308.                    ulrc = ConvertToMM ( ulpInstance,
  309.                                         (PULONG) &ulTemp1,
  310.                                         pPlayParms->ulFrom );
  311.  
  312.                    /************************************
  313.                    * Our to point must be less than
  314.                    * what we have recorded so far, and it
  315.                    * must be less than the length of the
  316.                    * file!
  317.                    **************************************/
  318.  
  319.                    if ( ulTemp1 > (ULONG) ulpInstance->StreamInfo.mmStreamTime &&
  320.                         ulTemp1 > ulFileLength )
  321.                      {
  322.                      return ( MCIERR_OUTOFRANGE);
  323.                      }
  324.                    } /* If the card is in record mode */
  325.  
  326.                 else
  327.                   {
  328.  
  329.                   /**********************************************************
  330.                   * Save an mmtime version of the from position, so that the
  331.                   * the MCI_TO checks below will ensure that to to position
  332.                   * is valid.
  333.                   **********************************************************/
  334.  
  335.                    ulrc = ConvertToMM ( ulpInstance,
  336.                                         &ulFromPosition,
  337.                                         pPlayParms->ulFrom );
  338.                   }
  339.  
  340.                 } /* If the stream is in a prerolled stated--i.e. buffers full */
  341.  
  342.             } /* else from < length && ! in record mode */
  343.  
  344.          } /* Play From flag was specified */
  345.  
  346.       /************************************************************
  347.       * If the caller specified the MCI_TO flag, then the following
  348.       * checks will be required:
  349.       *
  350.       * If we currently have a playback stream then:
  351.       *   A. Is the to position greater than the file length?
  352.       *   B. Is the to position greater than the from postion
  353.       *      (if and only if) MCI_FROM was specified.
  354.       *
  355.       * If a record stream is active then:
  356.       *   A. If it has not been started, then the to point
  357.       *      must not be 0 and must be greater than the from.
  358.       *   A. Else it must be less than both the file
  359.       *      length AND what we have recorded so far.
  360.       ************************************************************/
  361.  
  362.       if (ulParam1 & MCI_TO)
  363.          {
  364.          /***************************************
  365.          * If the to position is greater than
  366.          * the file length (using the currently
  367.          * specfied time format) return an error.
  368.          ****************************************/
  369.  
  370.          if ( pPlayParms->ulTo > ulTemp1 &&
  371.               AMPMIX.ulOperation != OPERATION_RECORD )
  372.             {
  373.             return ( MCIERR_OUTOFRANGE );
  374.             }
  375.          else
  376.             {
  377.  
  378.             /* If to position is <= from position--it's an error */
  379.  
  380.             if ( ulParam1 & MCI_FROM )
  381.                {
  382.                if ( pPlayParms->ulTo <= pPlayParms->ulFrom )
  383.                   {
  384.                   return ( MCIERR_OUTOFRANGE );
  385.                   }
  386.  
  387.                } /* if from flag was specified */
  388.  
  389.             /****************************************
  390.             * Was the stream previously started by a
  391.             * record, cue or play?
  392.             *****************************************/
  393.  
  394.             if ( ulpInstance->ulCreateFlag != PREROLL_STATE )
  395.                {
  396.  
  397.                /* As long as they don't want to play to 0 -- we are ok */
  398.  
  399.                if ( pPlayParms->ulTo == 0 )
  400.                  {
  401.                  return ( MCIERR_OUTOFRANGE );
  402.                  }
  403.  
  404.                return ( MCIERR_SUCCESS );
  405.  
  406.                } /* Stream not in preroll state */
  407.  
  408.             /*****************************************
  409.             * Determine our current stream time--use
  410.             * as a reference point for future checks.
  411.             ******************************************/
  412.  
  413.             ulrc = SpiGetTime( ulpInstance->StreamInfo.hStream,
  414.                                ( PMMTIME ) &( ulpInstance->StreamInfo.mmStreamTime ) );
  415.  
  416.             if ( ulrc )
  417.                {
  418.                return ( ulrc );
  419.                }
  420.             /*********************************************************
  421.             * The documentation states that the play will start from
  422.             * either the current position or the from point.
  423.             * If the from flag was not passed in, then set the
  424.             * from position to be equivalent to our current position
  425.             * for simplicity purposes.
  426.             * Note: ulFromPosition was filled in the from processing
  427.             *********************************************************/
  428.  
  429.             if ( ulFromPosition == 0 &&
  430.                  !(ulParam1 & MCI_FROM ) )
  431.                {
  432.                ulFromPosition = ( ULONG ) ulpInstance->StreamInfo.mmStreamTime;
  433.                }
  434.  
  435.  
  436.             if ( AMPMIX.ulOperation == OPERATION_RECORD )
  437.                {
  438.                /*******************************************
  439.                * Convert the to request to mmtime so we
  440.                * can compare it with the stream time.
  441.                * The new TO time will be place in ulTempTO
  442.                ********************************************/
  443.  
  444.                ulrc = ConvertToMM ( ulpInstance,
  445.                                     &ulTempTO,
  446.                                     pPlayParms->ulTo);
  447.  
  448.                /******************************************************
  449.                * If we are past what we have recorded so far and past
  450.                * the length of the file (in case of recording in the
  451.                * middle or start of the file), then return an error
  452.                *******************************************************/
  453.  
  454.                if ( ulTempTO > (ULONG ) ulpInstance->StreamInfo.mmStreamTime &&
  455.                     ulTempTO > ulFileLength )
  456.                  {
  457.                  return ( MCIERR_OUTOFRANGE );
  458.                  }
  459.                } /*  if we are in record mode */
  460.  
  461.             else
  462.                {
  463.                /*******************************************
  464.                * Convert the to request to mmtime so we
  465.                * can compare it with the stream time.
  466.                * The new TO time will be place in ulTempTO
  467.                ********************************************/
  468.  
  469.                ulrc = ConvertToMM ( ulpInstance,
  470.                                     &ulTempTO,
  471.                                     pPlayParms->ulTo);
  472.  
  473.                /************************************************
  474.                * We cannot play to a point which is behind our
  475.                * current position or to a point which is less
  476.                * than our from position.
  477.                ************************************************/
  478.  
  479.                if ( ulTempTO <= ( ULONG ) ulpInstance->StreamInfo.mmStreamTime &&
  480.                     ulTempTO <= ulFromPosition )
  481.                  {
  482.                  /********************************************************
  483.                  * it is possible that we had rounding problems so ensure
  484.                  * that user did indeed pass in an illegal value
  485.                  ********************************************************/
  486.  
  487.                  if ( ( ulParam1 & MCI_FROM ) &&
  488.                       !(pPlayParms->ulTo <= pPlayParms->ulFrom ) )
  489.                     {
  490.                     }
  491.                  else
  492.                     {
  493.                     return ( MCIERR_OUTOFRANGE );
  494.                     }
  495.  
  496.                  }
  497.                }
  498.  
  499.             } /* else to < file length */
  500.  
  501.          } // Of Play Till XXXX
  502.  
  503.   return (MCIERR_SUCCESS);
  504.  
  505. } /* Check play flags */
  506.  
  507.  
  508.  
  509.  
  510. /********************* START OF SPECIFICATIONS *********************
  511. *
  512. * SUBROUTINE NAME: MCIPlay
  513. *
  514. * DESCRIPTIVE NAME: Waveform Play Routine.
  515. *
  516. * FUNCTION: Play an Waveform File.
  517. *
  518. * NOTES:  hStream[1] = A --> B = Playback stream.
  519. *
  520. * ENTRY POINTS:
  521. *     LINKAGE:   CALL FAR
  522. *
  523. * INPUT: MCI_PLAY message.
  524. *
  525. * EXIT-NORMAL: Return Code 0.
  526. *
  527. * EXIT_ERROR:  Error Code.
  528. *
  529. * EFFECTS:
  530. *
  531. * INTERNAL REFERENCES:   ADMCERR (). CreateNAssocStream ().
  532. *
  533. * EXTERNAL REFERENCES:  spiStartStream ()    - SSM  Spi
  534. *                       spiStopStream  ()    - SSM  Spi
  535. *
  536. *********************** END OF SPECIFICATIONS **********************/
  537. RC MCIPlay (FUNCTION_PARM_BLOCK *pFuncBlock)
  538.  
  539. {
  540.  
  541.    ULONG         ulParam1;           // Flags for this Msg
  542.    ULONG         ulrc;               // Error Code Propogated
  543.    ULONG         lCnt;               // Number Of Posts
  544.    ULONG         ulAbortNotify  = FALSE;    // whether or not to abort notify's
  545.  
  546.    INSTANCE      *ulpInstance;       // Local Instance
  547.  
  548.    BOOL          fInitNeeded = FALSE;// must we reinit the card?
  549.    BOOL          fSeekNeeded = FALSE;// is a seek needed after a create
  550.    LONG          rc;                 // thread id return code
  551.  
  552.    /**************************************
  553.    * Intialize Variables on Stack
  554.    **************************************/
  555.    ulParam1 = pFuncBlock->ulParam1;
  556.  
  557.    /**************************************
  558.    * Derefernce Pointers
  559.    **************************************/
  560.    ulpInstance = (INSTANCE *) pFuncBlock->ulpInstance;
  561.  
  562.  
  563.    /*************************************************
  564.    * Check to ensure that all of the flags are valid
  565.    * memory is properly allocated and that the
  566.    * requested action is possible (i.e. the from
  567.    * position < to position etc.)
  568.    **************************************************/
  569.  
  570.    ulrc = CheckPlayFlags ( pFuncBlock );
  571.  
  572.    if (ulrc)
  573.       {
  574.       return (ulrc);
  575.       }
  576.  
  577.    /****************************************
  578.    * If no stream has been created, and the
  579.    * card has not changed modes, there is no
  580.    * need to reinit it!
  581.    *****************************************/
  582.  
  583.    if ( AMPMIX.ulOperation == OPERATION_RECORD  ||
  584.         ulpInstance->StreamInfo.ulState == STREAM_SET_STATE )
  585.       {
  586.       fInitNeeded = TRUE;
  587.       }
  588.  
  589.  
  590.    /*****************************************
  591.    * To ensure proper syncronization, acquire
  592.    * the semaphore which is used to control
  593.    * who can check to see which processes are
  594.    * active. This function will also tell us
  595.    * if there is an operation to abort or
  596.    * supercede.
  597.    ******************************************/
  598.  
  599.    GetNotifyAbortAccess ( ulpInstance, &ulAbortNotify );
  600.  
  601.  
  602.    /*******************************************
  603.    * If there is an operation active (i.e. a
  604.    * play, record or save) then post a message
  605.    * stating that the command has been
  606.    * aborted (record), superceded (play) or
  607.    * wait for completion (save).
  608.    ********************************************/
  609.  
  610.    if ( ulAbortNotify == TRUE)
  611.       {
  612.       ulrc = AbortInProgressNotify( ulpInstance, pFuncBlock, ulParam1, MCI_PLAY );
  613.       }
  614.  
  615.  
  616.    /***************************************************
  617.    * If the audio card and stream are setup for
  618.    * recording, then retrieve the old stream position,
  619.    * destroy the stream, and set a flag to indicate that
  620.    * the stream must be created for playback.
  621.    ****************************************************/
  622.  
  623.    if (AMPMIX.ulOperation == OPERATION_RECORD)
  624.        {
  625.  
  626.        /***********************************************
  627.        * Since we are destroying an existing stream
  628.        * retrieve the current stream position.  WHen
  629.        * the playback stream is created, we will
  630.        * seek to this point.  Do this so that the
  631.        * caller at the MCI Layer is ignorant of the
  632.        * streaming operations.
  633.        ***********************************************/
  634.  
  635.        GetOldStreamPosition( ulpInstance );
  636.  
  637.  
  638.        DestroyStream ( &ulpInstance->StreamInfo.hStream);
  639.        if ( ulpInstance->ulOldStreamPos > 0 )
  640.           {
  641.           fSeekNeeded = TRUE;
  642.           }
  643.        ulpInstance->ulCreateFlag = CREATE_STATE;
  644.  
  645.        }      /* Transition from Recd State */
  646.  
  647.  
  648.  
  649.    /***********************************************
  650.    * If a set was performed on an existing stream,
  651.    * destroy the stream and get new spcb keys.  You
  652.    * MUST recreate the stream after and MCI_SET has
  653.    * been done since there is no way to communicate
  654.    * to the stream handler the change in data rates
  655.    * other than at spiCreate time.
  656.    ***********************************************/
  657.  
  658.    DestroySetStream ( ulpInstance );
  659.  
  660.    /* Check to see if a stream has been created */
  661.  
  662.    if (ulpInstance->ulCreateFlag != PREROLL_STATE)
  663.        {
  664.  
  665.        /*******************************
  666.        * Do stream set up work and then
  667.        * create the stream
  668.        *******************************/
  669.  
  670.        ulrc = PrepareAndCreateStream( ulpInstance, OPERATION_PLAY, fInitNeeded );
  671.  
  672.        if ( ulrc )
  673.           {
  674.           return ( ulrc );
  675.           }
  676.  
  677.        /***************************************************************
  678.        * Set the stream up with the same position advises and cuepoints
  679.        * as the previous stream had (if there any).
  680.        ***************************************************************/
  681.  
  682.        ulrc = RememberStreamState( ulpInstance, ulParam1, fSeekNeeded );
  683.  
  684.        if ( ulrc )
  685.           {
  686.           return ( ulrc );
  687.           }
  688.  
  689.        }  /* PreRoll State */
  690.  
  691.    /***************************
  692.    * Set a flag which indicates
  693.    * that a stream has been
  694.    * created.
  695.    ****************************/
  696.    ulpInstance->ulCreateFlag = PREROLL_STATE;
  697.  
  698.    /******************************************************************
  699.    * Place the stream in the correct position if MCI_FROM is specified
  700.    * and set the correct stopping point if MCI_TO is specfied
  701.    ******************************************************************/
  702.  
  703.    ulrc = ProcessFromToFlags( pFuncBlock, ulpInstance, MCI_PLAY, ulParam1 );
  704.  
  705.    if ( ulrc )
  706.       {
  707.       return ( ulrc );
  708.       }
  709.  
  710.  
  711.   /****************************************
  712.   * See if the network can support the
  713.   * the file we are about to start streaming.
  714.   *****************************************/
  715.  
  716.   ulrc = BeginQualityofService( ulpInstance, STREAM_READ );
  717.  
  718.   /*-----------------------------------------
  719.   * If there is not a network io proc, we will
  720.   * receive unsupported message.  In this case
  721.   * we will ignore the error.
  722.   *
  723.   * If there is a network io proc we may have
  724.   * to examine the error a little more carefully
  725.   *--------------------------------------------*/
  726.  
  727.   if ( ulrc && ulrc != MMIOERR_UNSUPPORTED_MESSAGE )
  728.      {
  729.      /*--------------------------------------------
  730.      * At open time, we retrieved a variable called
  731.      * MSV_SYSQOSERRORFLAG which indicates if the
  732.      * user wants to be notified if the streaming
  733.      * operation will not be possible. It can
  734.      * have the following values:
  735.      *
  736.      * A. ERROR_REPORT (report all errors).
  737.      * B. ERROR_IGNORE (ignore all errors).
  738.      *---------------------------------------------*/
  739.  
  740.      if ( ulpInstance->lQOSReporting == ERROR_REPORT )
  741.         {
  742.         /* Tell the caller of the network bandwidth problem */
  743.  
  744.         return ( ulrc );
  745.         }
  746.      } /* If there was a problem setting up network support */
  747.  
  748.  
  749.    /***************************************
  750.    * Enable Position Advise if Needed
  751.    ****************************************/
  752.  
  753.    ulrc = EnableEvents( ulpInstance );
  754.  
  755.  
  756.    /************************************************
  757.    ** To determine whether or not an operation must be
  758.    ** done on a thread, use the following criteria:
  759.    **
  760.    **   A. Will the operation take a long time.
  761.    **   B. Is there any way to make it appear as if
  762.    **      a thread was active.
  763.    **
  764.    ** We could do an spiStart, receive the event
  765.    ** in our event proc and clean up on the SSM
  766.    ** thread.  However, this thread will be a HIGH
  767.    ** PRIORITY thread and we have too much processing
  768.    ** to do.  Therefore, there is no way to fake it.
  769.    ** Thus we must create a play thread if the notify
  770.    ** flag is sent
  771.    ************************************************/
  772.  
  773.    if (!ulrc)
  774.        {
  775.  
  776.        if (ulParam1 & MCI_NOTIFY)
  777.           {
  778.            ulpInstance->usNotifyPending = TRUE;
  779.            ulpInstance->usNotPendingMsg = MCI_PLAY;
  780.  
  781.           /****************************************************
  782.           * This thread is kicked off by the MCD mainly
  783.           * to start the stream. When it is safe to continue
  784.           * the play thread will post the thread semaphore and
  785.           * this thread can continue.
  786.           ****************************************************/
  787.  
  788.            DosResetEventSem (ulpInstance->hThreadSem, &lCnt);
  789.  
  790.            /* Note: I think this is too large */
  791.  
  792.            rc = _beginthread ( (PFNTHREAD)StartPlay,
  793.                                0,
  794.                                NOTIFY_THREAD_STACKSIZE,
  795.                                (ULONG) pFuncBlock );
  796.  
  797.            /*************************************************
  798.            * Wait for the play thread to indicate that it is
  799.            * safe to continue.
  800.            **************************************************/
  801.            if ( rc != -1 )
  802.               {
  803.               DosWaitEventSem (ulpInstance->hThreadSem, -1);
  804.               }
  805.            else
  806.               {
  807.               /****************************************
  808.               * Tell the network that we are done
  809.               * streaming since an error occurred
  810.               *****************************************/
  811.  
  812.               EndQualityofService( ulpInstance );
  813.  
  814.               ulrc = MCIERR_OUT_OF_MEMORY;
  815.               }
  816.           }
  817.  
  818.        /* The caller used MCI_WAIT, so do everything on one thread */
  819.  
  820.        else
  821.           {
  822.           ulpInstance->usWaitPending = TRUE;
  823.           ulpInstance->usWaitMsg = MCI_PLAY;
  824.  
  825.           /************************************************
  826.           * Start playback.  This function will kick
  827.           * off the play, get an event (i.e. stream
  828.           * complete, stopped etc.) and report the return
  829.           * code.  We will return this return code to the
  830.           * caller.
  831.           ************************************************/
  832.  
  833.           ulrc = StartPlay (pFuncBlock);
  834.           }
  835.  
  836.        } /* If no errors so far */
  837.  
  838.    DosResetEventSem (ulpInstance->hThreadSem, &lCnt);
  839.  
  840.  
  841.    return (ulrc);
  842. }
  843.  
  844.  
  845.  
  846.  
  847. /********************* START OF SPECIFICATIONS *********************
  848. *
  849. * SUBROUTINE NAME: EventProc.
  850. *
  851. * DESCRIPTIVE NAME: SSM Event Notifications Receiever.
  852. *
  853. * FUNCTION:  Handle Streaming Event Notifications from SSM.
  854. *
  855. * NOTES: This routine is presumed to receive all types of event
  856. *        notifications from SSM. The types include Implicit
  857. *        events, Cue point notifications in terms of both time
  858. *        and data. In response to Cue point notifications an
  859. *        MCI_CUEPOINT message is returned to MDM via mdmDriverNotify ()
  860. *
  861. * ENTRY POINTS:
  862. *
  863. * INPUT:
  864. *
  865. * EXIT-NORMAL: Return Code 0.
  866. *
  867. * EXIT_ERROR:  Error Code and flError flag is set.
  868. *
  869. * EFFECTS:
  870. *
  871. * INTERNAL REFERENCES:
  872. *
  873. * EXTERNAL REFERENCES: mdmDriverNotify ()  - MDM   API
  874. *
  875. *********************** END OF SPECIFICATIONS **********************/
  876.  
  877.  
  878. RC APIENTRY PlayEventRoutine ( MEVCB     *pevcb)
  879. {
  880.   MTIME_EVCB        *pMTimeEVCB;      // Modified Time EVCB
  881.   INSTANCE          * ulpInstance;    // Current Instance
  882.   HWND              hWnd;             // CallBack Handle
  883.  
  884.   BOOL              fPlayListDone = FALSE;
  885.  
  886.   /***********************************************************
  887.   * EventProc receives asynchronous SSM event notifications
  888.   * When the event is received, the event semaphore is posted
  889.   * which will wake up the MCD thread(s) blocked on this
  890.   * semaphore.
  891.   * The semaphore is not posted for time events like
  892.   * cuepoint (TIME) and media position changes since they do
  893.   * not alter the state of the stream.
  894.   ************************************************************/
  895.  
  896.   switch (pevcb->evcb.ulType)
  897.   {
  898.   case EVENT_IMPLICIT_TYPE:
  899.  
  900.        /* Retrieve our instance from the EVCB */
  901.  
  902.        ulpInstance = (INSTANCE *)pevcb->ulpInstance;
  903.  
  904.        /* Retrieve the callback handle to post messages on */
  905.  
  906.        hWnd = ulpInstance->hwndCallBack;
  907.  
  908.        switch (pevcb->evcb.ulSubType)
  909.        {
  910.        case EVENT_EOS:
  911.             ulpInstance->StreamEvent = EVENT_EOS;
  912.             DosPostEventSem (ulpInstance->hEventSem);
  913.            break;
  914.  
  915.        case EVENT_ERROR:
  916.  
  917.  
  918.             /****************************************
  919.             * Check for playlist specific error first
  920.             *****************************************/
  921.  
  922.             if (ulpInstance->usPlayLstStrm == TRUE)
  923.                {
  924.                if ( pevcb->evcb.ulStatus == MCIERR_CUEPOINT_LIMIT_REACHED )
  925.                   {
  926.                   mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  927.                                     hWnd,
  928.                                     MM_MCINOTIFY,
  929.                                     (USHORT )pevcb->evcb.unused1,
  930.                                     MAKEULONG(MCI_PLAY, pevcb->evcb.ulStatus));
  931.                   }
  932.                fPlayListDone = TRUE;
  933.                }
  934.  
  935.             /********************************************
  936.             * Because the target stream handler is able
  937.             * to recover from underruns, we will let the
  938.             * stream continue, other error are lethal, so
  939.             * alert the waiting thread that something has
  940.             * seriously gone wrong.
  941.             *********************************************/
  942.  
  943.             if (pevcb->evcb.ulStatus != ERROR_DEVICE_UNDERRUN)
  944.                {
  945.                ulpInstance->StreamEvent = EVENT_ERROR;
  946.                DosPostEventSem (ulpInstance->hEventSem);
  947.                }
  948.             else if ( !fPlayListDone )
  949.                {
  950.                mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  951.                                  hWnd,
  952.                                  MM_MCINOTIFY,
  953.                                  (USHORT) pevcb->evcb.unused1,
  954.                                  MAKEULONG(MCI_PLAY, pevcb->evcb.ulStatus));
  955.                }
  956.  
  957.            break;
  958.  
  959.        case EVENT_STREAM_STOPPED:
  960.             /* Self explanatory--someone stopped the stream */
  961.  
  962.             ulpInstance->StreamEvent = EVENT_STREAM_STOPPED;
  963.             DosPostEventSem (ulpInstance->hEventSem);
  964.            break;
  965.  
  966.        case EVENT_SYNC_PREROLLED:
  967.             /******************************************
  968.             * This event is received in reponse to a
  969.             * preroll start. A Preroll start is done
  970.             * on an MCI_CUE message.
  971.             *******************************************/
  972.  
  973.             ulpInstance->StreamEvent = EVENT_SYNC_PREROLLED;
  974.             DosPostEventSem (ulpInstance->hEventSem);
  975.            break;
  976.  
  977.        case EVENT_PLAYLISTMESSAGE:
  978.  
  979.             /******************************************
  980.             * We can receive this event if a playlist
  981.             * parser hits the MESSAGE COMMAND.
  982.             * NOTE: The MCD should return this message
  983.             * with the callback handle specified on the
  984.             * open.  This could be the source of much
  985.             * grief if you return on the wrong handle.
  986.             ******************************************/
  987.  
  988.             mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  989.                               ulpInstance->hwndOpenCallBack,
  990.                               MM_MCIPLAYLISTMESSAGE,
  991.                               (USHORT) MAKEULONG(pevcb->evcb.ulStatus, ulpInstance->usWaveDeviceID),
  992.                               (ULONG) pevcb->evcb.unused1);
  993.            break;
  994.  
  995.        case EVENT_PLAYLISTCUEPOINT:
  996.  
  997.             /************************************************
  998.             * We can receive this event if a playlist
  999.             * parser hits the CUEPOINT COMMAND opcode
  1000.             * in the playlist.  This differs from a "normal"
  1001.             * cuepoint because it is detected by the source,
  1002.             * rather than the target stream handler.
  1003.             ************************************************/
  1004.  
  1005.  
  1006.             mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  1007.                               ulpInstance->hwndOpenCallBack,
  1008.                               MM_MCICUEPOINT,
  1009.                               (USHORT) MAKEULONG(pevcb->evcb.ulStatus, ulpInstance->usWaveDeviceID),
  1010.                               (ULONG) pevcb->evcb.unused1);
  1011.            break;
  1012.  
  1013.  
  1014.        } /* SubType case of Implicit Events */
  1015.       break;
  1016.  
  1017.   case EVENT_CUE_TIME_PAUSE:
  1018.        {
  1019.        /***************************************************
  1020.        * This event will arrive if we played to a certain
  1021.        * position in the stream.  Let the play thread know
  1022.        * that we have reached the desired point.
  1023.        ****************************************************/
  1024.  
  1025.        pMTimeEVCB = (MTIME_EVCB *)pevcb;
  1026.        ulpInstance = (INSTANCE *)pMTimeEVCB->ulpInstance;
  1027.        ulpInstance->StreamEvent = EVENT_CUE_TIME_PAUSE;
  1028.  
  1029.        DosPostEventSem (ulpInstance->hEventSem);
  1030.        }
  1031.        break;
  1032.  
  1033.   case EVENT_CUE_TIME:
  1034.        {
  1035.        pMTimeEVCB  = (MTIME_EVCB *)pevcb;
  1036.        ulpInstance = (INSTANCE *)pMTimeEVCB->ulpInstance;
  1037.  
  1038.        /*************************************************
  1039.        * Single Events are Treated as Time Cue Points
  1040.        * Note: the caller is required to have a callback
  1041.        * specifically for this purpose which we stored in
  1042.        * our Time Event Control Block (EVCB).  See the
  1043.        * ADMCCUE.C file for more information on how to
  1044.        * manipulate your own EVCB and why you would want to
  1045.        **************************************************/
  1046.  
  1047.        if ( pMTimeEVCB->evcb.ulFlags == EVENT_SINGLE)
  1048.            {
  1049.            mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  1050.                              pMTimeEVCB->hwndCallback,
  1051.                              MM_MCICUEPOINT,
  1052.                              (USHORT) pMTimeEVCB->usCueUsrParm,
  1053.                              (ULONG) pMTimeEVCB->evcb.mmtimeStream);
  1054.            }
  1055.  
  1056.        /************************************************
  1057.        * Recurring events equate to position advise events
  1058.        * or media changed events.
  1059.        *
  1060.        * Note: the caller is required to have a callback
  1061.        * specifically for this purpose which we stored in
  1062.        * our Time Event Control Block (EVCB).  See the
  1063.        * ADMCCUE.C file for more information on how to
  1064.        * manipulate your own EVCB and why you would want to
  1065.        **************************************************/
  1066.  
  1067.        if (pMTimeEVCB->evcb.ulFlags == EVENT_RECURRING)
  1068.           {
  1069.           mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  1070.                             ulpInstance->StreamInfo.PosAdvEvcb.hwndCallback,
  1071.                             MM_MCIPOSITIONCHANGE,
  1072.                             (USHORT) ulpInstance->usPosUserParm,
  1073.                             (ULONG) pMTimeEVCB->evcb.mmtimeStream);
  1074.           } /* Event Cue Time */
  1075.  
  1076.        }
  1077.        break;
  1078.  
  1079.  
  1080.   } /* All Events case */
  1081.  
  1082.   return (MCIERR_SUCCESS);
  1083.  
  1084. } /* PlayEventProc */
  1085.  
  1086.  
  1087.  
  1088.  
  1089. /********************* START OF SPECIFICATIONS *******************************
  1090. *
  1091. * SUBROUTINE NAME: StartPlay
  1092. *
  1093. * DESCRIPTIVE NAME:Start Play
  1094. *
  1095. * FUNCTION: Start the playback, wait till it is over and finish processing.
  1096. *           If MCI_NOTIFY is used, also post a message.
  1097. *
  1098. *
  1099. * NOTES: This routine is called using caller' thread (MCI_WAIT)
  1100. *        or a separate thread spawned by MCD on MCI Notify.  Once the stream
  1101. *        is started, the event procedure above is called by SSM on a
  1102. *        high priority thread.  The event procedure will post a semaphore
  1103. *        awaking this function.
  1104. *
  1105. *        When a streaming operation is interuptted (usually by a stop)
  1106. *        the interuptting thread waits for the MCDs thread to complete
  1107. *        its remaing tasks. This wait is controlled via the instance based
  1108. *        thread semaphore.
  1109. *
  1110. *        Further, on a notify the Play Notify command does not return
  1111. *        until the newly created thread is ready to block itself. This
  1112. *        ensures that any other MCI messages that are free to be intercepted
  1113. *        following the MCI_PLAY message operate on a running stream.
  1114. *        This also means there is minimum latency between the return of the
  1115. *        Play command to the application and start of audible sound.
  1116. *
  1117. * ENTRY POINTS:
  1118. *
  1119. * INPUT:
  1120. *
  1121. * EXIT-NORMAL: MCIERR_SUCCESS
  1122. *
  1123. * EXIT_ERROR:  Error Code.
  1124. *
  1125. * EFFECTS:
  1126. *
  1127. * INTERNAL REFERENCES: None
  1128. *
  1129. * EXTERNAL REFERENCES: None
  1130. *
  1131. *********************** END OF SPECIFICATIONS *******************************/
  1132.  
  1133. RC  StartPlay (FUNCTION_PARM_BLOCK * pFuncBlockCopy )
  1134. {
  1135.  
  1136.   ULONG         ulrc;
  1137.   ULONG         lCnt;
  1138.   ULONG         ulParam1;
  1139.  
  1140.  
  1141.   INSTANCE      *ulpInstance;
  1142.   ULONG         ulErr;
  1143.   FUNCTION_PARM_BLOCK  FuncBlock;
  1144.  
  1145.   BOOL          fPostMessage;       // should the caller be informed
  1146.                                     // about the stream state
  1147.  
  1148.  
  1149.   memmove( &FuncBlock, pFuncBlockCopy, sizeof( FUNCTION_PARM_BLOCK ) );
  1150.  
  1151.   ulrc = MCIERR_SUCCESS;           // is this assignment needed?
  1152.   ulErr = MCI_NOTIFY_SUCCESSFUL;
  1153.   ulpInstance = (INSTANCE *) FuncBlock.pInstance;
  1154.   ulParam1 = FuncBlock.ulParam1;
  1155.   fPostMessage = TRUE;
  1156.  
  1157.   /**************************************
  1158.   * Reset the event semaphore used by the
  1159.   * PlayEventProc.  See comments below.
  1160.   ****************************************/
  1161.  
  1162.   DosResetEventSem (ulpInstance->hEventSem, &lCnt);
  1163.  
  1164.  
  1165.   /* Update state to reflect the flact we are playing */
  1166.  
  1167.   ulpInstance->StreamInfo.ulState = MCI_PLAY;
  1168.  
  1169.   /****************************************
  1170.   * Set a flag so we can sense when someone
  1171.   * issues an aborted or superceded request
  1172.   *****************************************/
  1173.  
  1174.   ulpInstance->ulNotifyAborted = FALSE;
  1175.  
  1176.  
  1177.   /****************************
  1178.   * Start Playing the Stream.
  1179.   *****************************/
  1180.  
  1181.   ulrc = SpiStartStream (ulpInstance->StreamInfo.hStream, SPI_START_STREAM);
  1182.  
  1183.   /*****************************************
  1184.   * If this is a notify thread, let the
  1185.   * caller know it is ok to continue
  1186.   *****************************************/
  1187.  
  1188.   if (ulParam1 & MCI_NOTIFY)
  1189.      {
  1190.      FuncBlock.pInstance->usUserParm = FuncBlock.usUserParm;
  1191.      DosPostEventSem (ulpInstance->hThreadSem);
  1192.      }
  1193.  
  1194.  
  1195.   /***********************************************
  1196.   * It is VERY important to check the error code
  1197.   * from the SpiStartStream before waiting on an
  1198.   * event from it.  If an error is returned and
  1199.   * we wait on an event from the event proc, we
  1200.   * will hang since an event will never come.
  1201.   ***********************************************/
  1202.  
  1203.   if ( ulrc )
  1204.      {
  1205.      /**********************************
  1206.      * Disable Notify Pending Flag
  1207.      ***********************************/
  1208.  
  1209.      if (ulpInstance->usNotifyPending == TRUE)
  1210.         {
  1211.         ulpInstance->usNotifyPending =FALSE;
  1212.         }
  1213.  
  1214.      /* Let the caller know things went VERY badly */
  1215.  
  1216.      if ( ulParam1 & MCI_NOTIFY )
  1217.         {
  1218.         PostMDMMessage (ulrc, MCI_PLAY, &FuncBlock);
  1219.         }
  1220.  
  1221.      return ( ulrc );
  1222.      }
  1223.  
  1224.  
  1225.  
  1226.   /**********************************************
  1227.   * Block this thread until an event happens in
  1228.   * the stream.  When it does, the PlayEventProc
  1229.   * will signal us via this semaphore.
  1230.   **********************************************/
  1231.  
  1232.   DosWaitEventSem (ulpInstance->hEventSem, (ULONG) -1);
  1233.  
  1234.   /*******************************************************
  1235.   * Acquire semaphore which will prevent any thread from
  1236.   * aborting us.  Once we have this semaphore, enter a
  1237.   * critical section, update some key instance variables
  1238.   * and get out quickly.
  1239.   *******************************************************/
  1240.  
  1241.   DosRequestMutexSem( ulpInstance->hmtxNotifyAccess, -1 );
  1242.  
  1243.   /**********************************************
  1244.   * Let any other thread know that there no
  1245.   * longer is a command active which can be
  1246.   * aborted.  We are in the clean up stage now
  1247.   * and once we exit the critical section, other
  1248.   * commands can safely operate.
  1249.   ***********************************************/
  1250.  
  1251.   if (ulpInstance->usNotifyPending == TRUE)
  1252.       ulpInstance->usNotifyPending =FALSE;
  1253.  
  1254.   /**************************************
  1255.   * Ensure that if we are being aborted
  1256.   * by another thread, and we received
  1257.   * an event that we ignore the event
  1258.   * and let the other process post the
  1259.   * notify
  1260.   **************************************/
  1261.  
  1262.   if (ulpInstance->ulNotifyAborted == TRUE)
  1263.      {
  1264.      fPostMessage = FALSE;
  1265.      }
  1266.  
  1267.  
  1268.   /* End of Stream (EOS) is good--the play is complete */
  1269.  
  1270.   if (ulpInstance->StreamEvent == EVENT_EOS)
  1271.      {
  1272.      ulErr = MCI_NOTIFY_SUCCESSFUL;
  1273.      }
  1274.  
  1275.   /************************************************
  1276.   * We have set up the event proc to give us the
  1277.   * more detailed error in ulStatus if an EVENT
  1278.   * ERROR is returned.
  1279.   *************************************************/
  1280.  
  1281.   else if (ulpInstance->StreamEvent == EVENT_ERROR)
  1282.      {
  1283.      ulErr = ulpInstance->StreamInfo.Evcb.evcb.ulStatus;
  1284.      DosResetEventSem (ulpInstance->hEventSem, &lCnt);
  1285.  
  1286.      /*********************************************
  1287.      * Because we are stopping the stream, we must
  1288.      * save the stream event that happened before
  1289.      * the stop, otherwise, the stopped event will
  1290.      * overwrite the old event and we will report
  1291.      * the wrong event via MDMDriverNotify
  1292.      **********************************************/
  1293.  
  1294.      ulrc = SpiStopStream (ulpInstance->StreamInfo.hStream, SPI_STOP_DISCARD );
  1295.  
  1296.      if (!ulrc)
  1297.        {
  1298.        DosWaitEventSem (ulpInstance->hEventSem, (ULONG) -1);
  1299.        }
  1300.  
  1301.      }
  1302.  
  1303.  
  1304.  
  1305.   /************************************************
  1306.   * Store the fact that we were doing a play to
  1307.   * a certain point in the stream.  Other threads
  1308.   * may overwrite this variable so keep its value
  1309.   * while we are in the critical section.
  1310.   ************************************************/
  1311.  
  1312.   if (ulpInstance->fToEvent == TRUE)
  1313.      {
  1314.  
  1315.      ulpInstance->fToEvent = FALSE;
  1316.  
  1317.      /*************************************************
  1318.      * NOTE: it is very important that we disable this
  1319.      * event since following commands could receive a
  1320.      * bogus cuepoint when the stream by the to point.
  1321.      **************************************************/
  1322.  
  1323.  
  1324.      SpiDisableEvent(ulpInstance->StreamInfo.hPlayToEvent);
  1325.  
  1326.      /***************************************************
  1327.      * Just because we have received the cue time event
  1328.      * does not mean that the stream is stopped.  If we
  1329.      * tried to seek in the current stream state, BAD
  1330.      * things will happen so stop the stream.
  1331.      ***************************************************/
  1332.  
  1333.      SpiStopStream (ulpInstance->StreamInfo.hStream, SPI_STOP_STREAM );
  1334.      }
  1335.  
  1336.  
  1337.   /******************************************************
  1338.   * Fix for MMPM/2 AVC--they do repeated close and after
  1339.   * approximately 80 closes, due to some OS/2 scheduling
  1340.   * quirk, close would free the instance before the thread
  1341.   * finished processing.  Therefore, require close to
  1342.   * acquire the exclusive semaphore before freeing
  1343.   * instance.
  1344.   ********************************************************/
  1345.  
  1346.   DosRequestMutexSem( ulpInstance->hmtxCloseAccess, -1 );
  1347.  
  1348.  
  1349.   /*******************************************************
  1350.   * Play back stream will perform an spi stop_pause
  1351.   * to ensure that if someone requested another playback
  1352.   * no information would be lost.  If you do a stop
  1353.   * discard/flush, the next playback may lose several
  1354.   * bytes of information.  Most people won't hear it,
  1355.   * but some may.
  1356.   *******************************************************/
  1357.  
  1358.   ulpInstance->StreamInfo.ulState = STOP_PAUSED;
  1359.  
  1360.   /****************************************
  1361.   * Tell the network that we are done
  1362.   * streaming.
  1363.   *****************************************/
  1364.  
  1365.   EndQualityofService( ulpInstance );
  1366.  
  1367.  
  1368.   /*********************************************
  1369.   * If the caller used MCI_WAIT, then return
  1370.   * without sending a message via MDMDriverNotify
  1371.   *********************************************/
  1372.  
  1373.   if (ulpInstance->usWaitPending == TRUE)
  1374.      {
  1375.      ulpInstance->usWaitPending = FALSE;
  1376.  
  1377.      DosReleaseMutexSem( ulpInstance->hmtxNotifyAccess );
  1378.  
  1379.      DosReleaseMutexSem( ulpInstance->hmtxCloseAccess );
  1380.      return ( ulErr );
  1381.      }
  1382.  
  1383.   /****************************************************
  1384.   * If we received an eos or an event error, then this
  1385.   * means this thread is terminated--inform the caller.
  1386.   * Note: the posting of the MCI_TO case was handled
  1387.   * above.
  1388.   *****************************************************/
  1389.  
  1390.  
  1391.   if ( fPostMessage )
  1392.        {
  1393.        PostMDMMessage (ulErr, MCI_PLAY, &FuncBlock);
  1394.        }
  1395.  
  1396.   /********************************************************
  1397.   * If another command (such as a record, stop, seek etc.)
  1398.   * tries to abort this play thread, they will wait on
  1399.   * the thread semaphore to ensure that we cleaned up
  1400.   * properly.  Therefore, post the semaphore to indicate
  1401.   * that we are done.
  1402.   ********************************************************/
  1403.  
  1404.   DosPostEventSem (ulpInstance->hThreadSem);
  1405.   
  1406.   /***************************************
  1407.   * Allow other threads in the process to
  1408.   * continue and release exlcusive notify
  1409.   * semaphore.
  1410.   ***************************************/
  1411.  
  1412.   DosReleaseMutexSem( ulpInstance->hmtxNotifyAccess );
  1413.  
  1414.   DosReleaseMutexSem( ulpInstance->hmtxCloseAccess );
  1415.  
  1416.   return (MCIERR_SUCCESS);
  1417.  
  1418. } /* StartPlay */
  1419.  
  1420.  
  1421.  
  1422.  
  1423.  
  1424.