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