home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / warptlk3.zip / TOOLKIT / SAMPLES / MM / ADMCT / ADMCRECD.C < prev    next >
C/C++ Source or Header  |  1995-08-24  |  53KB  |  1,608 lines

  1. /********************* START OF SPECIFICATIONS *********************
  2. *
  3. * SUBROUTINE NAME: MCIRECD.C
  4. *
  5. *
  6. *
  7. *              Copyright (c) IBM Corporation  1991, 1993
  8. *                        All Rights Reserved
  9. *
  10. * DESCRIPTIVE NAME: Audio MCD WaveAudio Recording Source.
  11. *
  12. * FUNCTION: Record into  Waveform Audio Element.
  13. *
  14. *  On MCI_RECORD, 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 (play or save) or superced (record) 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 playback)
  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 play back stream before creating the record stream,
  26. *   seek to the same position in the record stream where the play back stream
  27. *   was.
  28. *  Enable any events (such as cuepoints or position advises).
  29. *  Setup MMIO 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 play stream->record 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. Processing record insert.
  47. *         H. Determing when the creation of a thread is necessary
  48. *            for an MCI_NOTIFY
  49. *         I. How to use MMIO networking features (via MMIOM_BEGINSTREAM).
  50. *         J. Proper handling of a record event procedure.
  51. *         K. Proper termination of a record thread.
  52. *         L. Communicating with MMIO for retrieving/seting file header.
  53. *         M. Communicating with MMIO to process recording/inserting.
  54. *
  55. *
  56. * ENTRY POINTS:
  57. *
  58. * INPUT: MCI_Record message.
  59. *        MCI_FROM Flag
  60. *        MCI_TO   Flag
  61. *        MCI_RECORD_OVERWRITE   Flag
  62. *        MCI_RECORD_INSERT      Flag
  63. *
  64. * EXIT-NORMAL: Return Code 0.
  65. *
  66. * EXIT_ERROR:  Error Code.
  67. *
  68. * EFFECTS:
  69. *           The Original Element may be modified.
  70. *           All Recording is lost if MCI_SAVE message is not specified.
  71. *
  72. *
  73. * INTERNAL REFERENCES:   CreateNAssocStream ().
  74. *                        DestroyStream ().
  75. *                        AssocMemPlayToAudioStream ().
  76. *                        CreateToEvent().
  77. *                        ConvertTimeUnits ().
  78. *                        StartRecord().
  79. *                        ReEventProc().
  80. *                        SetAudioDevice().
  81. *                        SetWaveDeviceDefaults().
  82. *                        OpenFile().
  83. *                        CheckMem ().
  84. *
  85. * EXTERNAL REFERENCES:   DosResetEventSem ()        - OS/2 API
  86. *                        DosPostEventSem  ()        - OS/2 API
  87. *                        DosCreateThread ()         - OS/2 API
  88. *                        SpiEnableEvent ()          - MME API
  89. *                        SpiStopStream ()           - MME API
  90. *                        SpiCreateStream ()         - MME API
  91. *                        SpiAssociate()             - MME API
  92. *                        SpiSeekStream ()           - MME API
  93. *                        mdmDriverNotify ()         - MME API
  94. *                        mmioSetHeader ()           - MME API
  95. *
  96. *********************** END OF SPECIFICATIONS **********************/
  97. #define INCL_BASE
  98. #define INCL_DOSMODULEMGR
  99. #define INCL_DOSSEMAPHORES
  100.  
  101. #include <os2.h>
  102. #include <string.h>
  103. #include <stdlib.h>                     // begin thread prototype
  104. #include <os2medef.h>                   // MME includes files.
  105. #include <ssm.h>                        // SSM spi includes.
  106. #include <meerror.h>                    // MM Error Messages.
  107. #include <mmioos2.h>                    // MMIO Include.
  108. #include <mcios2.h>                     // MM System Include.
  109. #include <mmdrvos2.h>                     // Mci Driver Include.
  110. #include <mcd.h>                        // VSDIDriverInterface.
  111. #include <hhpheap.h>                    // Heap Manager Definitions
  112. #include <qos.h>
  113. #include <audiomcd.h>                   // Component Definitions.
  114. #include "admcfunc.h"                   // Function Prototypes
  115. #include <checkmem.h>
  116.  
  117. /********************* START OF SPECIFICATIONS *********************
  118. *
  119. * SUBROUTINE NAME: MCIRECD.C
  120. *
  121. * DESCRIPTIVE NAME: Waveform Record Routine.
  122. *
  123. * FUNCTION: Record into an Waveform File.
  124. *
  125. * NOTES:  hStream[2] = B --> A = Record stream.
  126. *
  127. * ENTRY POINTS:
  128. *     LINKAGE:   CALL FAR
  129. *
  130. * INPUT: MCI_PLAY message.
  131. *
  132. * EXIT-NORMAL: Return Code 0.
  133. *
  134. * EXIT_ERROR:  Error Code.
  135. *
  136. * EFFECTS:
  137. *
  138. * INTERNAL REFERENCES:  CreateNAssocStream ().
  139. *                       ().
  140. *                       OpenFile ().
  141. *                       SetAudioDevice ().
  142. *                       SetWaveDeviceDefaults ().
  143. *                       AssocMemPlayToAudioStream ().
  144. *
  145. * EXTERNAL REFERENCES:  SpiStartStream ()    - SSM  Spi
  146. *                       SpiStopStream  ()    - SSM  Spi
  147. *
  148. *********************** END OF SPECIFICATIONS **********************/
  149. RC MCIRecd (FUNCTION_PARM_BLOCK *pFuncBlock)
  150.  
  151. {
  152.   ULONG      ulrc;               // Propogated Error Code
  153.   ULONG      ulParam1;           // Incoming MCI Flags
  154.   ULONG      ulCnt;              // Sem Posting Count
  155.  
  156.   ULONG      ulAbortNotify = FALSE;
  157.  
  158.  
  159.   INSTANCE   *ulpInstance;       // Local Instance
  160.  
  161.   BOOL       fInitNeeded;        // Must the card be reinited
  162.   BOOL       fSeekNeeded = FALSE;// is a seek needed after a create
  163.  
  164. //  MCI_AMP_INSTANCE     BackupAmp;           // Hold old amp values
  165.  
  166.   LONG       rc;                 // thread it return
  167.  
  168.   /********************************
  169.   * derefernce pointers
  170.   *********************************/
  171.   ulpInstance = (INSTANCE *)pFuncBlock->ulpInstance;
  172.   ulParam1 = pFuncBlock->ulParam1;
  173.  
  174.  
  175.   /*---------------------------------------------------------
  176.   * Make a copy of the amp/mixer instance in case any errors
  177.   * happened.
  178.   *---------------------------------------------------------*/
  179. // 6421--no more need of this
  180. //  memmove( &BackupAmp, &MIX, sizeof( MCI_AMP_INSTANCE ) );
  181.  
  182.  
  183.  
  184.   /*************************************************
  185.   * Check to ensure that all of the flags are valid
  186.   * memory is properly allocated and that the
  187.   * requested action is possible (i.e. the from
  188.   * position < to position, etc.)
  189.   **************************************************/
  190.  
  191.   ulrc = CheckRecordFlags ( pFuncBlock );
  192.  
  193.   /*******************************************
  194.   * If there are any errors, we must release
  195.   * the access semaphore which will allow
  196.   * other commands to process.  If we do not
  197.   * release this semaphore, the next command
  198.   * (i.e. MCI_PLAY, MCI_SET etc., will be
  199.   * blocked waiting for it to be posted.
  200.   *******************************************/
  201.  
  202.   if (ulrc)
  203.      {
  204.      return (ulrc);
  205.      }
  206.  
  207.  
  208.    /********************************************************
  209.    * In order for a play to be possible, the straming MCD must
  210.    * be connected to another MCD (i.e. the file's output must
  211.    * go to another device.
  212.    * Ensure that this is true.
  213.    ********************************************************/
  214.  
  215.    CheckForConnection( ulpInstance );
  216.  
  217.  
  218.  
  219.   /****************************************
  220.   * If no stream has been created, and the
  221.   * card has not changed modes, there is no
  222.   * need to reinit it!
  223.   *****************************************/
  224.  
  225.   if ( ulpInstance->ulOperation == MCIDRV_OUTPUT  ||
  226.        ulpInstance->StreamInfo.ulState == STREAM_SET_STATE )
  227.      {
  228.      fInitNeeded = TRUE;
  229.      }
  230.  
  231.    /***********************************************
  232.    * If all of the parameters are valid so far, then
  233.    * obtain semaphore which allows us to abort or
  234.    * supercede in progress commands.
  235.    ************************************************/
  236.  
  237.    GetNotifyAbortAccess ( ulpInstance, &ulAbortNotify );
  238.  
  239.   /*******************************************
  240.   * If there is an operation active (i.e. a
  241.   * play, record or save) then post a message
  242.   * stating that the command has been
  243.   * aborted (play), superceded (record) or
  244.   * wait for completion (save).
  245.   ********************************************/
  246.  
  247.    if ( ulAbortNotify == TRUE)
  248.       {
  249.       ulrc = AbortInProgressNotify( ulpInstance, pFuncBlock, ulParam1, MCI_RECORD );
  250.       }
  251.  
  252.  
  253.  
  254.  
  255.   /**********************************************************************
  256.   * Destroy the stream. If a play preceded, the direction of the stream is
  257.   * reversed. The previous stream gets destroyed.
  258.   **********************************************************************/
  259.  
  260.   if (ulpInstance->ulOperation == MCIDRV_OUTPUT)
  261.       {
  262.        /***********************************************
  263.        * Since we are destroying an existing stream
  264.        * retrieve the current stream position.  When
  265.        * the record stream is created, we will
  266.        * seek to this point.  Do this so that the
  267.        * caller at the MCI Layer is ignorant of the
  268.        * streaming operations.
  269.        ***********************************************/
  270.  
  271.  
  272.       GetOldStreamPosition( ulpInstance);
  273. #ifndef CONNECTION
  274.       DestroyStream ( ulpInstance);
  275.       if ( ulpInstance->ulRealTimeTranslation ==MMIO_REALTIME )
  276.          {
  277.          ulpInstance->ulCodecDescription = MAKE_TARGET | TARGET_CODEC;
  278.          DataTranslation( ulpInstance );
  279.          }
  280. #endif
  281.       ulpInstance->ulCreateFlag = CREATE_STATE;
  282.  
  283.  
  284.  
  285.  
  286.       if ( ulpInstance->ulOldStreamPos > 0 )
  287.          {
  288.          fSeekNeeded = TRUE;
  289.          }
  290.       }      /* Transition from PLAY State */
  291.  
  292.    /***********************************************
  293.    * If a set was performed on an existing stream,
  294.    * destroy the stream and get new spcb keys.  You
  295.    * MUST recreate the stream after and MCI_SET has
  296.    * been done since there is no way to communicate
  297.    * to the stream handler the change in data rates
  298.    * other than at spiCreate time.
  299.    ***********************************************/
  300.  
  301.    DestroySetStream ( ulpInstance );
  302.  
  303.   /*********************************************************************
  304.   * Create The Recording stream. This normally goes from audio stream
  305.   * handler to the file system stream handler. Based on the associate
  306.   * flag we can change the recording destination to File system or
  307.   * memory (in case of playlist)
  308.   *********************************************************************/
  309.  
  310.   if (ulpInstance->ulCreateFlag != PREROLL_STATE)
  311.  
  312.      {
  313.      /*******************************
  314.      * Do stream set up work and then
  315.      * create the stream
  316.      *******************************/
  317.  
  318.      ulrc = PrepareAndCreateStream( ulpInstance, MCIDRV_INPUT, fInitNeeded );
  319.  
  320.      if ( ulrc )
  321.         {
  322.         /* Ensure that our instance remains the same as before the load attempt */
  323.  
  324.         /* Truly--I have no idea why this code is here--6421 */
  325.  
  326. //        memmove( &MIX, &BackupAmp, sizeof ( MCI_AMP_INSTANCE ) );
  327.  
  328.         return ( ulrc );
  329.         }
  330.  
  331.  
  332.      /***************************************************************
  333.      * Set the stream up with the same position advises and cuepoints
  334.      * as the previous stream had (if there any).
  335.      ***************************************************************/
  336.  
  337.      ulrc = RememberStreamState( ulpInstance, ulParam1, fSeekNeeded );
  338.  
  339.      if ( ulrc )
  340.         {
  341.         return ( ulrc );
  342.         }
  343.      }  /* Create Flag != Preroll State */
  344.  
  345.   /**********************
  346.   * Update State Flags
  347.   ***********************/
  348.   ulpInstance->ulCreateFlag = PREROLL_STATE;
  349.  
  350.   /*************************************************
  351.   * Playlist currently do not support io procs, thus
  352.   * they currently do not support MMIO calls.
  353.   * Therefore, if we are currently using a playlist
  354.   * then we cannot work with the audio header in the
  355.   * file.
  356.   **************************************************/
  357.  
  358. //  if (ulpInstance->usPlayLstStrm != TRUE)
  359. //      {
  360. //
  361. //      /******************************************************************
  362. //      * Before we start to record, set the header of the file via MMIO.
  363. //      * This will allow applications which opened the file via OPEN_MMIO
  364. //      * to allways have a correct view of what is in the file.
  365. //      ******************************************************************/
  366. //
  367. //      ulrc = SetAudioHeader (ulpInstance);
  368. //
  369. //      } /* Non PlayList */
  370.  
  371.  
  372.    /******************************************************************
  373.    * Place the stream in the correct position if MCI_FROM is specified
  374.    * and set the correct stopping point if MCI_TO is specfied
  375.    ******************************************************************/
  376.  
  377.    ulrc = ProcessFromToFlags( pFuncBlock, ulpInstance, MCI_RECORD, ulParam1 );
  378.  
  379.    if ( ulrc )
  380.       {
  381.       return ( ulrc );
  382.       }
  383.  
  384.  
  385.   /***********************************************
  386.   * If the caller requests that we record insert,
  387.   * then let the IO Procedure know that we want
  388.   * the record to be done via insert until we
  389.   * turn this flag off (at the end of StartRecord)
  390.   ************************************************/
  391.  
  392.   if (ulParam1 & MCI_RECORD_INSERT)
  393.      {
  394.      /* Not all IO Procs can insert, check capabilities first */
  395.  
  396.       if ( ulpInstance->ulCapabilities & CAN_INSERT )
  397.          {
  398.          /* Request record insert from mmio io proc */
  399.  
  400.          ulrc = mmioSendMessage( ulpInstance->hmmio,
  401.                                  MMIOM_BEGININSERT,
  402.                                  0,
  403.                                  0);
  404.          if (ulrc)
  405.             {
  406.             /* If an error, get a little more info via mmioGetLastError */
  407.  
  408.             ulrc = mmioGetLastError( ulpInstance->hmmio );
  409.  
  410.             return (ulrc);
  411.             }
  412.  
  413.            /* Set flag indicating that record insert is active */
  414.  
  415.            ulpInstance->fRecdInsert = TRUE;
  416.  
  417.          } /* Current IO Proc can insert */
  418.       else
  419.          {
  420.  
  421.          return ( MCIERR_UNSUPPORTED_FLAG );
  422.  
  423.          } /* Current IO Proc cannot insert */
  424.  
  425.      } /* Record Insert flag sent */
  426.  
  427.   else if ( !ulpInstance->usPlayLstStrm )
  428.  
  429.      {
  430.      ulrc = mmioSendMessage( ulpInstance->hmmio,
  431.                              MMIOM_BEGINGROUP,
  432.                              0,
  433.                              0);
  434.      if (ulrc)
  435.         {
  436.         ulrc = mmioGetLastError( ulpInstance->hmmio );
  437.  
  438.         return (ulrc);
  439.         }
  440.  
  441.  
  442.      } /* NORMAL RECORD */
  443.  
  444.  
  445.   /***********************************************
  446.   * Ensure that our avg. bytes per sec is accurate
  447.   * since a best fit could have changed the field
  448.   * on the fly.
  449.   ***********************************************/
  450.  
  451. //  ulpInstance->ulAverageBytesPerSec = WAVEHDR.usChannels * WAVEHDR.ulSamplesPerSec * ( WAVEHDR.usBitsPerSample / 8 );
  452.   ulpInstance->ulAverageBytesPerSec =
  453.    ( ULONG ) ( WAVEHDR.ulSamplesPerSec * ( WAVEHDR.usBitsPerSample / 8 ) * WAVEHDR.usChannels );
  454.  
  455.  
  456.   /****************************************
  457.   * See if the network can support the
  458.   * the file we are about to start streaming.
  459.   *
  460.   * ALL streaming MCD's should take advantage
  461.   * of this MMIO api.
  462.   *****************************************/
  463.  
  464.   ulrc = BeginQualityofService( ulpInstance, STREAM_WRITE );
  465.  
  466.   /*-----------------------------------------
  467.   * If there is not a network io proc, we will
  468.   * receive unsupported message.  In this case
  469.   * we will ignore the error.
  470.   *
  471.   * If there is a network io proc we may have
  472.   * to examine the error a little more carefully
  473.   *--------------------------------------------*/
  474.  
  475.   if ( ulrc && ulrc != MMIOERR_UNSUPPORTED_MESSAGE )
  476.      {
  477.      /*--------------------------------------------
  478.      * At open time, we retrieved a variable called
  479.      * MSV_SYSQOSERRORFLAG which indicates if the
  480.      * user wants to be notified if the streaming
  481.      * operation will not be possible. It can
  482.      * have the following values:
  483.      *
  484.      * A. ERROR_REPORT (report all errors).
  485.      * B. ERROR_IGNORE (ignore all errors).
  486.      *---------------------------------------------*/
  487.  
  488.      if ( ulpInstance->lQOSReporting == ERROR_REPORT )
  489.         {
  490.         /* Tell the caller of the network bandwidth problem */
  491.  
  492.         return ( ulrc );
  493.         }
  494.      } /* If there was a problem setting up network support */
  495.  
  496.  
  497.   /************************************
  498.   * Enable Position Advise if Needed
  499.   *************************************/
  500.  
  501.  
  502.   ulrc = EnableEvents( ulpInstance );
  503.  
  504.  
  505.  
  506.   if (!ulrc)
  507.        /************************************************
  508.        ** To determine whether or not an operation must be
  509.        ** done on a thread, use the following criteria:
  510.        **
  511.        **   A. Will the operation take a long time.
  512.        **   B. Is there any way to make it appear as if
  513.        **      a thread was active.
  514.        **
  515.        ** We could do an spiStart, receive the event
  516.        ** in our event proc and clean up on the SSM
  517.        ** thread.  However, this thread will be a HIGH
  518.        ** PRIORITY thread and we have too much processing
  519.        ** to do.  Therefore, there is no way to fake it.
  520.        ** Thus we must create a record thread if the notify
  521.        ** flag is sent
  522.        ************************************************/
  523.  
  524.       if (ulParam1 & MCI_NOTIFY)
  525.          {
  526.          ulpInstance->usNotifyPending = TRUE;
  527.          ulpInstance->usNotPendingMsg = MCI_RECORD;
  528.  
  529.          DosResetEventSem (ulpInstance->hThreadSem, &ulCnt);
  530.  
  531.           /****************************************************
  532.           * This thread is kicked off by the MCD mainly
  533.           * to start the stream. When it is safe to continue
  534.           * the play thread will post the thread semaphore and
  535.           * this thread can continue.
  536.           ****************************************************/
  537.  
  538.           rc = _beginthread ( (ADMCTHREAD) StartRecord,
  539.                                  0,
  540.                                  NOTIFY_THREAD_STACKSIZE,
  541.                                (void *)pFuncBlock );
  542.  
  543.          /*************************************************
  544.          * Wait for the record thread to indicate that it
  545.          * is safe to continue.
  546.          **************************************************/
  547.  
  548.            if ( rc != -1 )
  549.               {
  550.               DosWaitEventSem (ulpInstance->hThreadSem, -1);
  551.               }
  552.            else
  553.               {
  554.               /****************************************
  555.               * Tell the network that we are done
  556.               * streaming since an error occurred
  557.               *****************************************/
  558.  
  559.               EndQualityofService( ulpInstance );
  560.  
  561.               ulrc = MCIERR_OUT_OF_MEMORY;
  562.               }
  563.  
  564.  
  565.          if ( rc > 0 )
  566.             {
  567.             DosWaitEventSem (ulpInstance->hThreadSem, -1);
  568.             }
  569.          }
  570.  
  571.       else
  572.          {
  573.          ulpInstance->usWaitPending = TRUE;
  574.          ulpInstance->usWaitMsg= MCI_RECORD;
  575.  
  576.          /************************************************
  577.          * Start record.  This function will kick
  578.          * off the record, get an event (i.e. stream
  579.          * complete, stopped etc.) and report the return
  580.          * code.  We will return this return code to the
  581.          * caller.
  582.          ************************************************/
  583.  
  584.  
  585.          ulrc = StartRecord (pFuncBlock);
  586.  
  587.          } /* MCI_WAIT flag was used */
  588.  
  589.   /**********************************
  590.   * Release data Access Semaphore
  591.   ***********************************/
  592.  
  593.    DosResetEventSem (ulpInstance->hThreadSem, &ulCnt);
  594.  
  595.   return (ulrc);
  596. }
  597.  
  598.  
  599.  
  600. /********************* START OF SPECIFICATIONS *********************
  601. *
  602. * SUBROUTINE NAME: EventProc.
  603. *
  604. * DESCRIPTIVE NAME: SSM Event Handler
  605. *
  606. * FUNCTION:  Handle Streaming Event Notifications from SSM.
  607. *
  608. * NOTES: This routine is presumed to receive all types of event
  609. *        notifications from the SSM. The types include Implicit
  610. *        events, Cue point notifications in terms of both time
  611. *        and data. In response to Cue point notifications an
  612. *        MCI_CUEPOINT message is returned to MDM via mdmDriverNotify ()
  613. *
  614. * ENTRY POINTS:
  615. *
  616. * INPUT:
  617. *
  618. * EXIT-NORMAL: Return Code 0.
  619. *
  620. * EXIT_ERROR:  Error Code and flError flag is set.
  621. *
  622. * EFFECTS:
  623. *
  624. * INTERNAL REFERENCES:
  625. *
  626. * EXTERNAL REFERENCES: mdmDriverNotify ()  - MDM   API
  627. *
  628. *********************** END OF SPECIFICATIONS **********************/
  629. RC APIENTRY RecordEventRoutine ( MEVCB  *pevcb)
  630. {
  631.   MTIME_EVCB        *pMTimeEVCB;    // Modified EVCB
  632.   INSTANCE          *ulpInstance;   // Instance Ptr
  633.  
  634.   /***********************************************************
  635.   * EventProc receives asynchronous SSM event notifications
  636.   * When the event is received, the event semaphore is posted
  637.   * which will wake up the MCD thread(s) blocked on this
  638.   * semaphore.
  639.   * The semaphore is not posted for time events like
  640.   * cuepoint (TIME) and media position changes since they do
  641.   * not alter the state of the stream.
  642.   ************************************************************/
  643.  
  644.  
  645.   switch (pevcb->evcb.ulType)
  646.   {
  647.   case EVENT_IMPLICIT_TYPE:
  648.  
  649.        /* Retrieve our instance from the EVCB */
  650.  
  651.        ulpInstance = (INSTANCE *)pevcb->ulpInstance;
  652.  
  653.        switch (pevcb->evcb.ulSubType)
  654.        {
  655.        // 6421--its possible for us to now get EOS (i.e. fssh->fssh ).
  656.        case EVENT_EOS:
  657.             ulpInstance->StreamEvent = EVENT_EOS;
  658.             DosPostEventSem (ulpInstance->hEventSem);
  659.            break;
  660.  
  661.        case EVENT_ERROR:
  662.             ulpInstance->StreamEvent = EVENT_ERROR;
  663.  
  664.             /****************************************
  665.             * Check for playlist specific error first
  666.             *****************************************/
  667.  
  668.             /**************************************
  669.             * End of PlayList event is received
  670.             * as an implicit error event. It
  671.             * is treated as a normal EOS
  672.             ***************************************/
  673.             if (ulpInstance->usPlayLstStrm == TRUE)
  674.                 if (pevcb->evcb.ulStatus == ERROR_END_OF_PLAYLIST)
  675.                     ulpInstance->StreamInfo.Evcb.evcb.ulStatus = MMIOERR_CANNOTWRITE;
  676.  
  677.             DosPostEventSem (ulpInstance->hEventSem);
  678.            break;
  679.  
  680.        case EVENT_STREAM_STOPPED:
  681.             /****************************************
  682.             * Event Stream Stopped. Release the
  683.             * Blocked thread
  684.             *****************************************/
  685.             ulpInstance->StreamEvent = EVENT_STREAM_STOPPED;
  686.             DosPostEventSem (ulpInstance->hEventSem);
  687.            break;
  688.  
  689.        case EVENT_SYNC_PREROLLED:
  690.             /******************************************
  691.             * This event is received in reponse to a
  692.             * preroll start. A Preroll start is done
  693.             * on an MCI_CUE message.
  694.             *******************************************/
  695.             ulpInstance->StreamEvent = EVENT_SYNC_PREROLLED;
  696.             DosPostEventSem (ulpInstance->hEventSem);
  697.            break;
  698.  
  699.        case EVENT_PLAYLISTMESSAGE:
  700.             /******************************************
  701.             * We can receive this event if a playlist
  702.             * parser hits the MESSAGE COMMAND.
  703.             * NOTE: The MCD should return this message
  704.             * with the callback handle specified on the
  705.             * open.  This could be the source of much
  706.             * grief if you return on the wrong handle.
  707.             ******************************************/
  708.  
  709.             mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  710.                               ulpInstance->hwndOpenCallBack,
  711.                               MM_MCIPLAYLISTMESSAGE,
  712.                               (USHORT) MAKEULONG(pevcb->evcb.ulStatus,
  713.                                                  ulpInstance->usWaveDeviceID),
  714.                               (ULONG)pevcb->evcb.unused1);
  715.  
  716.  
  717.            break;
  718.  
  719.        /* Note: playlist cuepoints are not supported on record -- see docs */
  720.  
  721.        } /* SubType case of Implicit Events */
  722.       break;
  723.  
  724.   case EVENT_CUE_TIME_PAUSE:
  725.        /***************************************************
  726.        * This event will arrive if we recorded to a certain
  727.        * position in the stream.  Let the play thread know
  728.        * that we have reached the desired point.
  729.        ****************************************************/
  730.  
  731.       pMTimeEVCB = (MTIME_EVCB *)pevcb;
  732.       ulpInstance = (INSTANCE *)pMTimeEVCB->ulpInstance;
  733.       ulpInstance->StreamEvent = EVENT_CUE_TIME_PAUSE;
  734.  
  735.       DosPostEventSem (ulpInstance->hEventSem);
  736.       break;
  737.   case EVENT_CUE_TIME:
  738.  
  739.        /*************************************************
  740.        * Single Events are Treated as Time Cue Points
  741.        * Note: the caller is required to have a callback
  742.        * specifically for this purpose which we stored in
  743.        * our Time Event Control Block (EVCB).  See the
  744.        * ADMCCUE.C file for more information on how to
  745.        * manipulate your own EVCB and why you would want to
  746.        **************************************************/
  747.  
  748.        pMTimeEVCB = (MTIME_EVCB *)pevcb;
  749.        ulpInstance = (INSTANCE *)pMTimeEVCB->ulpInstance;
  750.  
  751.       if ( pMTimeEVCB->evcb.ulFlags == EVENT_SINGLE )
  752.          {
  753.          mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  754.                            pMTimeEVCB->hwndCallback,
  755.                            MM_MCICUEPOINT,
  756.                            (USHORT) pMTimeEVCB->usCueUsrParm,
  757.                            (ULONG) pMTimeEVCB->evcb.mmtimeStream);
  758.          }
  759.  
  760.        /************************************************
  761.        * Recurring events equate to position advise events
  762.        * or media changed events.
  763.        *
  764.        * Note: the caller is required to have a callback
  765.        * specifically for this purpose which we stored in
  766.        * our Time Event Control Block (EVCB).  See the
  767.        * ADMCCUE.C file for more information on how to
  768.        * manipulate your own EVCB and why you would want to
  769.        **************************************************/
  770.  
  771.       if ( pMTimeEVCB->evcb.ulFlags == EVENT_RECURRING )
  772.          {
  773.          mdmDriverNotify ( ulpInstance->usWaveDeviceID,
  774.                            ulpInstance->StreamInfo.PosAdvEvcb.hwndCallback,
  775.                            MM_MCIPOSITIONCHANGE,
  776.                            (USHORT) ulpInstance->usPosUserParm,
  777.                            (ULONG)  pMTimeEVCB->evcb.mmtimeStream);
  778.          }
  779.  
  780.  
  781.       break;
  782. #ifdef STATUS_LEVEL
  783.   case EVENT_STATUS_LEVEL:
  784.        StatusLevelEvent (ulpInstance, pevcb);
  785.        break;
  786. #endif
  787.   default:
  788.       break;
  789.  
  790.   }  /* All Events case */
  791.  
  792.   return (MCIERR_SUCCESS);
  793.  
  794. }              /* of Event Handler */
  795.  
  796.  
  797.  
  798.  
  799.  
  800. /********************* START OF SPECIFICATIONS *******************************
  801. *
  802. * SUBROUTINE NAME: StartRecord.
  803. *
  804. * DESCRIPTIVE NAME:StartRecording
  805. *
  806. * FUNCTION: SpiStartStream().
  807. *
  808. *
  809. * ENTRY POINTS:
  810. *
  811. * NOTES: This routine is called using caller' thread (MCI_WAIT)
  812. *        or a separate thread spawned by MCD on MCI Notify.  Once the stream
  813. *        is started, the event procedure above is called by SSM on a
  814. *        high priority thread.  The event procedure will post a semaphore
  815. *        awaking this function.
  816. *
  817. *        When a streaming operation is interuptted (usually by a stop)
  818. *        the interuptting thread waits for the MCDs thread to complete
  819. *        its remaing tasks. This wait is controlled via the instance based
  820. *        thread semaphore.
  821. *
  822. *        Further, on a notify the Record Notify command does not return
  823. *        until the newly created thread is ready to block itself. This
  824. *        ensures that any other MCI messages are free to be processed
  825. *        following the MCI_RECORD message operate on a running stream.
  826. *        This also means there is minimum latency between the return of the
  827. *        Record command to the application and start of audible sound.
  828. *
  829. * INPUT:
  830. *
  831. * EXIT-NORMAL: Return Code 0.
  832. *
  833. * EXIT_ERROR:  Error Code.
  834. *
  835. * EFFECTS:
  836. *
  837. * INTERNAL REFERENCES: None
  838. *
  839. * EXTERNAL REFERENCES: None
  840. *
  841. *********************** END OF SPECIFICATIONS *******************************/
  842. RC _Optlink  StartRecord (VOID * pvoid)
  843. {
  844.   FUNCTION_PARM_BLOCK * pFuncBlockCopy = (FUNCTION_PARM_BLOCK *) pvoid;
  845.   ULONG         ulrc;
  846.   ULONG         ulCnt;
  847.   ULONG         ulParam1;
  848.   ULONG         ulErr;
  849. //  ULONG         ulRecdToNotify;
  850.   INSTANCE      *ulpInstance;
  851.  
  852.   FUNCTION_PARM_BLOCK  FuncBlock;
  853.  
  854.   BOOL          fPostMessage;      // should we inform the caller?
  855.  
  856.   memmove( &FuncBlock, pFuncBlockCopy, sizeof( FUNCTION_PARM_BLOCK ) );
  857.  
  858.    ulErr = MCI_NOTIFY_SUCCESSFUL;
  859.    ulpInstance = (INSTANCE *) FuncBlock.ulpInstance;
  860.    ulParam1 = FuncBlock.ulParam1;
  861.    fPostMessage = TRUE;
  862.  
  863.  
  864.   /**************************************
  865.   * Reset the event semaphore used by the
  866.   * RecordEventProc.  See comments below.
  867.   ****************************************/
  868.  
  869.    DosResetEventSem (ulpInstance->hEventSem, &ulCnt);
  870.  
  871.    /* Update state to reflect the flact we are playing */
  872.  
  873.    ulpInstance->StreamInfo.ulState = MCI_RECORD;
  874.  
  875.  
  876.   /****************************************
  877.   * Set a flag so we can sense when someone
  878.   * issues an aborted or superceded request
  879.   *****************************************/
  880.  
  881.   ulpInstance->ulNotifyAborted = FALSE;
  882.  
  883.  
  884.  
  885.    /***********************
  886.    * Start the stream
  887.    ************************/
  888. // CONNECTOR FEATURE
  889. // ulrc = SpiStartStream (ulpInstance->StreamInfo.hStream, SPI_START_STREAM);
  890.   ulrc = StartStream( ulpInstance, MCIDRV_START_RECORD );
  891. // CONNECTOR FEATURE
  892.  
  893.  
  894.   /*****************************************
  895.   * If this is a notify thread, let the
  896.   * caller know it is ok to continue
  897.   *****************************************/
  898.  
  899.   if (ulParam1 & MCI_NOTIFY)
  900.      {
  901.      FuncBlock.pInstance->usUserParm = FuncBlock.usUserParm;
  902.      DosPostEventSem (ulpInstance->hThreadSem);
  903.      }
  904.  
  905.   /***********************************************
  906.   * It is VERY important to check the error code
  907.   * from the SpiStartStream before waiting on an
  908.   * event from it.  If an error is returned and
  909.   * we wait on an event from the event proc, we
  910.   * will hang since an event will never come.
  911.   ***********************************************/
  912.  
  913.    if ( !ulrc )
  914.       {
  915.       /**********************************************
  916.       * Block this thread until an event happens in
  917.       * the stream.  When it does, the PlayEventProc
  918.       * will signal us via this semaphore.
  919.       **********************************************/
  920.  
  921.       DosWaitEventSem (ulpInstance->hEventSem, (ULONG) -1);
  922.  
  923.       /*******************************************************
  924.       * Acquire semaphore which will prevent any thread from
  925.       * aborting us.  Once we have this semaphore, enter a
  926.       * critical section, update some key instance variables
  927.       * and get out quickly.
  928.       *******************************************************/
  929.  
  930.       DosRequestMutexSem( ulpInstance->hmtxNotifyAccess, -1 );
  931.  
  932.       /**********************************************
  933.       * Let any other thread know that there no
  934.       * longer is a command active which can be
  935.       * aborted.  We are in the clean up stage now
  936.       * and once we exit the critical section, other
  937.       * commands can safely operate.
  938.       ***********************************************/
  939.  
  940.       if (ulpInstance->usNotifyPending == TRUE)
  941.           ulpInstance->usNotifyPending =FALSE;
  942.  
  943.       /**************************************
  944.       * Ensure that if we are being aborted
  945.       * by another thread, and we received
  946.       * an event that we ignore the event
  947.       * and let the other process post the
  948.       * notify
  949.       **************************************/
  950.  
  951.       if (ulpInstance->ulNotifyAborted == TRUE)
  952.          {
  953.          fPostMessage = FALSE;
  954.          }
  955.  
  956.       /************************************************
  957.       * We have set up the event proc to give us the
  958.       * more detailed error in ulStatus if an EVENT
  959.       * ERROR is returned.
  960.       *************************************************/
  961.  
  962.       else if (ulpInstance->StreamEvent == EVENT_ERROR)
  963.         {
  964.         ulErr = ulpInstance->StreamInfo.Evcb.evcb.ulStatus;
  965.  
  966.         if (ulErr == MMIOERR_CANNOTWRITE)
  967.            {
  968.            ulErr = MCIERR_TARGET_DEVICE_FULL;
  969.            }
  970.  
  971.         /***************************************************
  972.         * Just because we have received an event error, it
  973.         * does not mean that the stream is stopped.  If we
  974.         * tried to seek in the current stream state, BAD
  975.         * things will happen so stop the stream.
  976.         ***************************************************/
  977.  
  978.         DosResetEventSem (ulpInstance->hEventSem, &ulCnt);
  979.  
  980.         /* An error happened, therefore do a discard--we're toast anyway */
  981.  
  982.         ulrc = ADMCStopStream (ulpInstance, SPI_STOP_DISCARD );
  983.  
  984.         if (!ulrc)
  985.            {
  986.            DosWaitEventSem (ulpInstance->hEventSem, (ULONG) -1);
  987.            }
  988.  
  989.          } /* if an event error happened */
  990.  
  991. #ifdef STATUS_LEVEL
  992.       /* clear the status level */
  993.       ulpInstance->ulStatusLevel = 0;               /* status_level */
  994. #endif
  995.       /************************************************
  996.       * Store the fact that we were doing a record to
  997.       * a certain point in the stream.  Other threads
  998.       * may overwrite this variable so keep its value
  999.       * while we are in the critical section.
  1000.       ************************************************/
  1001.  
  1002.       if (ulpInstance->fToEvent == TRUE)
  1003.          {
  1004.          ulpInstance->fToEvent = FALSE;
  1005.  
  1006.          /*************************************************
  1007.          * NOTE: it is very important that we disable this
  1008.          * event since following commands could receive a
  1009.          * bogus cuepoint when the stream by the to point.
  1010.          **************************************************/
  1011.  
  1012.          ulrc = ADMCDisableEvent(ulpInstance->StreamInfo.hPlayToEvent);
  1013.  
  1014.          DosResetEventSem (ulpInstance->hEventSem, &ulCnt);
  1015.  
  1016.          ulrc = ADMCStopStream (ulpInstance, SPI_STOP_FLUSH );
  1017.  
  1018.          if (!ulrc)
  1019.             {
  1020.             DosWaitEventSem (ulpInstance->hEventSem, (ULONG) -1);
  1021.             }
  1022.  
  1023.          } /* Were we recording to a certain point */
  1024.  
  1025.  
  1026.       } /* if no error on starting a record */
  1027.    else  /* an error occurred on starting the stream */
  1028.       {
  1029.       /**********************************
  1030.       * Disable Notify Pending Flag
  1031.       ***********************************/
  1032.  
  1033.       if (ulpInstance->usNotifyPending == TRUE)
  1034.         ulpInstance->usNotifyPending =FALSE;
  1035.  
  1036.       /* Let the caller know things went VERY badly */
  1037.  
  1038.       if ( ulParam1 & MCI_NOTIFY )
  1039.          {
  1040.          PostMDMMessage (ulrc, MCI_RECORD, &FuncBlock);
  1041.          }
  1042.  
  1043.       return ( ulrc );
  1044.       }
  1045.  
  1046.   /******************************************************
  1047.   * Fix for MMPM/2 AVC--they do repeated open\closes and after
  1048.   * approximately 80 closes, due to some OS/2 scheduling
  1049.   * quirk, close would free the instance before the thread
  1050.   * finished processing.  Therefore, require close to
  1051.   * acquire the exclusive semaphore before freeing
  1052.   * instance.
  1053.   ********************************************************/
  1054.  
  1055.   DosRequestMutexSem( ulpInstance->hmtxCloseAccess, -1 );
  1056.  
  1057.  
  1058.    /* Ensure the file contains the information we just recorded */
  1059.  
  1060.    ulrc = UpdateFileHeader( ulpInstance );
  1061.  
  1062.    if ( ulrc )
  1063.       {
  1064.       /*************************************************
  1065.       * If an error previously occurred, we do not want
  1066.       * to overwrite this error.
  1067.       **************************************************/
  1068.  
  1069.       ulErr = ulrc;
  1070.       }
  1071.  
  1072.  
  1073.   /********************************************
  1074.   * After a record, we always do a full-fledged
  1075.   * stop, so update the stream state.
  1076.   *********************************************/
  1077.   ulpInstance->StreamInfo.ulState = MCI_STOP;
  1078.  
  1079.   if (ulpInstance->usPlayLstStrm != TRUE)
  1080.      {
  1081.      /****************************************
  1082.      * Tell the network that we are done
  1083.      * streaming.
  1084.      *****************************************/
  1085.  
  1086.      EndQualityofService( ulpInstance );
  1087.  
  1088.  
  1089.      /*------------------------------------------
  1090.      * Records can be un-done.  Update this fact
  1091.      * in our undo/redo level storage.
  1092.      *------------------------------------------*/
  1093.      AddNode( ulpInstance, 1 );
  1094.      ulpInstance->ulNumUndo++;
  1095.      }
  1096.  
  1097.   /****************************************
  1098.   * Post The Notification only after
  1099.   * File is saved in case of To
  1100.   *****************************************/
  1101.  
  1102.   if (ulpInstance->usWaitPending == TRUE)
  1103.      {
  1104.      ulpInstance->usWaitPending = FALSE;
  1105.      DosReleaseMutexSem( ulpInstance->hmtxNotifyAccess );
  1106.      DosReleaseMutexSem( ulpInstance->hmtxCloseAccess );
  1107.  
  1108.      return (ulErr);
  1109.      }
  1110.  
  1111.  
  1112.    /****************************************************
  1113.    * Inform the caller of the state of the stream. The
  1114.    * only scenario where we do not do this is when
  1115.    * another command (such as MCI_STOP ) aborts the
  1116.    * record ( in which case it is responsible for
  1117.    * posting the message itself.
  1118.    *****************************************************/
  1119.  
  1120.     if ( fPostMessage )
  1121.          {
  1122.          /*******************************************
  1123.          * Post The Notification Message for Record
  1124.          ********************************************/
  1125.          PostMDMMessage (ulErr, MCI_RECORD, &FuncBlock);
  1126.          }
  1127.  
  1128.  
  1129.  
  1130.    /**************************************************
  1131.    * Post The Semaphore to clear any Blocked threads
  1132.    **************************************************/
  1133.    DosPostEventSem (ulpInstance->hThreadSem);
  1134.  
  1135.    /***************************************
  1136.    * Allow other threads in the process to
  1137.    * continue and release exlcusive notify
  1138.    * semaphore.
  1139.    ***************************************/
  1140.  
  1141.    DosReleaseMutexSem( ulpInstance->hmtxNotifyAccess );
  1142.  
  1143.    DosReleaseMutexSem( ulpInstance->hmtxCloseAccess );
  1144.  
  1145.    return ( MCIERR_SUCCESS );
  1146.  
  1147. } /* StartRecord */
  1148.  
  1149. /********************* START OF SPECIFICATIONS *******************************
  1150. *
  1151. * SUBROUTINE NAME: UpdateFileHeader.
  1152. *
  1153. * DESCRIPTIVE NAME:
  1154. *
  1155. * FUNCTION: Updates the file header after a record
  1156. *
  1157. *
  1158. * ENTRY POINTS:
  1159. *
  1160. * NOTES: This function is responsible for terminating the logical
  1161. *        record action and updating the file header.
  1162. *
  1163. * INPUT:
  1164. *
  1165. * EXIT-NORMAL: Return Code 0.
  1166. *
  1167. * EXIT_ERROR:  Error Code.
  1168. *
  1169. * EFFECTS:
  1170. *
  1171. * INTERNAL REFERENCES: None
  1172. *
  1173. * EXTERNAL REFERENCES: None
  1174. *
  1175. *********************** END OF SPECIFICATIONS *******************************/
  1176.  
  1177.  
  1178.  
  1179. ULONG UpdateFileHeader ( INSTANCE    *ulpInstance )
  1180.  
  1181. {
  1182. ULONG ulrc;
  1183. LONG  lBytesRead;
  1184.  
  1185.    /***************************************************
  1186.    * Unlike playback, record modifies the length of the
  1187.    * file and the data in it, therefore we MUST update
  1188.    * let the io proc know that the opeartion is done.
  1189.    *
  1190.    * Note: This does not apply to playlist since it
  1191.    * operates on buffers rather than files and mmio
  1192.    * operations are not allowed on playlists.
  1193.    ****************************************************/
  1194.  
  1195.    if ( ulpInstance->usPlayLstStrm != TRUE )
  1196.       {
  1197.       /************************************************
  1198.       * If the RECORD_INSERT flag was used, tell the
  1199.       * io proc we are done inserting
  1200.       ************************************************/
  1201.       if ( ulpInstance->fRecdInsert )
  1202.         {
  1203.  
  1204.         ulrc = mmioSendMessage( ulpInstance->hmmio,
  1205.                                 MMIOM_ENDINSERT,
  1206.                                 0,
  1207.                                 0);
  1208.  
  1209.         if (ulrc)
  1210.           {
  1211.           return ( mmioGetLastError( ulpInstance->hmmio ) );
  1212.           }
  1213.  
  1214.         /***************************************
  1215.         * Ensure that it no other record inserts
  1216.         * will be done unless specified
  1217.         ****************************************/
  1218.  
  1219.         ulpInstance->fRecdInsert = FALSE;
  1220.  
  1221.         }
  1222.       else
  1223.  
  1224.       /************************************************
  1225.       * Tell the io proc that the logical record is
  1226.       * complete.  This will allow the record to be
  1227.       * undone or redone.
  1228.       ***********************************************/
  1229.  
  1230.         {
  1231.         ulrc = mmioSendMessage( ulpInstance->hmmio,
  1232.                                 MMIOM_ENDGROUP,
  1233.                                 0,
  1234.                                 0);
  1235.  
  1236.         if (ulrc)
  1237.           {
  1238.           return ( mmioGetLastError( ulpInstance->hmmio ) );
  1239.           }
  1240.  
  1241.         }  /* else we are not inserting */
  1242.  
  1243.  
  1244.       /***************************************************
  1245.       * Unlike playback, record modifies the length of the
  1246.       * file and the data in it, therefore we MUST update
  1247.       * the header.
  1248.       ****************************************************/
  1249.  
  1250.       if ( ulrc = SetAudioHeader( ulpInstance ) )
  1251.          {
  1252.          return ( ulrc );
  1253.          }
  1254.  
  1255.  
  1256.       /***********************************************
  1257.       * The io proc will update the size of the file
  1258.       * after we do a setheader, so do a getheader to
  1259.       * figure out what the new size of the file is
  1260.       ************************************************/
  1261.       ulrc = mmioGetHeader ( ulpInstance->hmmio,
  1262.                              (PVOID) &(ulpInstance->mmAudioHeader),
  1263.                              sizeof( MMAUDIOHEADER ),
  1264.                              &lBytesRead,
  1265.                              (ULONG) NULL,
  1266.                              (ULONG) NULL );
  1267.  
  1268.       if (ulrc != MMIO_SUCCESS )
  1269.          {
  1270.          return ( mmioGetLastError( ulpInstance->hmmio ) );
  1271.          }
  1272.  
  1273.       /* update our header size */
  1274.  
  1275.       ulpInstance->ulDataSize = XWAVHDR.ulAudioLengthInBytes;
  1276.  
  1277.       } /* update header if !playlist stream */
  1278.  
  1279.     return ( MCIERR_SUCCESS );
  1280.  
  1281. } /* UpdateFileHeader */
  1282.  
  1283.  
  1284.  
  1285. /********************* START OF SPECIFICATIONS *******************************
  1286. *
  1287. * SUBROUTINE NAME: CheckRecordFlags
  1288. *
  1289. * DESCRIPTIVE NAME: Process Error Conditions for  Record
  1290. *
  1291. * FUNCTION: Report Errors, Do PreProcessing for MCI Record
  1292. *
  1293. *
  1294. * ENTRY POINTS:
  1295. *
  1296. * NOTE: All MCI Functions should check to ensure that the flags are
  1297. *       valid before interrupting a previous operation.  This means
  1298. *       that if the caller supplied bad flags while a play was
  1299. *       operating, the play would not be interrupted.
  1300. *
  1301. * EXIT-NORMAL: MCIERR_SUCCESS.
  1302. *
  1303. * EXIT_ERROR:  Error Code.
  1304. *
  1305. * EFFECTS:
  1306. *
  1307. * INTERNAL REFERENCES: WaveIOPROC.
  1308. *
  1309. * EXTERNAL REFERENCES: DosQueryProcAddr - OS/2 API.
  1310. *
  1311. *********************** END OF SPECIFICATIONS *******************************/
  1312.  
  1313. RC CheckRecordFlags( FUNCTION_PARM_BLOCK *pFuncBlock )
  1314.  
  1315. {
  1316.  
  1317.   ULONG                ulrc;                  // RC
  1318.   ULONG                ulParam1;              // Incoming MCI Flags
  1319.  
  1320.   INSTANCE             *ulpInstance;          // Local Instance
  1321.  
  1322.   ULONG                ulRecordFlags;         // Mask for MCI Record
  1323.   ULONG                ulMMFileLength;        // Length of the File in MMTIME
  1324.   ULONG                ulTemp1;               // Scratch For Time Conversion
  1325.   ULONG                ulTempTO = 0;          // Scratch for Play Till
  1326.   ULONG                ulFromPosition = 0;    // holds where we will play from
  1327.  
  1328.   PMCI_RECORD_PARMS   pRecordParms;         // caller data struct
  1329.  
  1330.  
  1331.  
  1332.   /* Intialize local variables */
  1333.  
  1334.   ulParam1 = pFuncBlock->ulParam1;
  1335.   ulpInstance = (INSTANCE *)pFuncBlock->ulpInstance;
  1336.  
  1337.  
  1338.  
  1339.   ulRecordFlags = pFuncBlock->ulParam1;
  1340.  
  1341.   /****************************************
  1342.   * Mask out the flags we know about
  1343.   ****************************************/
  1344.  
  1345.   ulRecordFlags &= ~(MCI_FROM + MCI_TO + MCI_RECORD_INSERT +
  1346.                      MCI_RECORD_OVERWRITE + MCI_WAIT + MCI_NOTIFY);
  1347.  
  1348.   /* Return an error if there are any excess flags */
  1349.  
  1350.   if (ulRecordFlags > 0)
  1351.       return ( MCIERR_INVALID_FLAG );
  1352.  
  1353.   /***********************************************
  1354.   * Check for incompatible flags.
  1355.   ************************************************/
  1356.  
  1357.   if( (pFuncBlock->ulParam1 & MCI_RECORD_INSERT) &&
  1358.        (pFuncBlock->ulParam1 & MCI_RECORD_OVERWRITE))
  1359.  
  1360.         return ( MCIERR_FLAGS_NOT_COMPATIBLE );
  1361.  
  1362.   /************************************************
  1363.   * If neither overwrite or insert is specified,
  1364.   * then we will default to overwrite.
  1365.   * NOTE: this is in contrast to what the docs say
  1366.   * invesitigate.
  1367.   ************************************************/
  1368.  
  1369.   if (!(pFuncBlock->ulParam1 & MCI_RECORD_INSERT))
  1370.       pFuncBlock->ulParam1 |= MCI_RECORD_OVERWRITE;
  1371.  
  1372.   /***********************************************
  1373.   * If no element has been loaded, return an
  1374.   * error.
  1375.   ***********************************************/
  1376.  
  1377.   if ( !ulpInstance->fFileExists )
  1378.       return ( MCIERR_FILE_NOT_FOUND );
  1379.  
  1380.   /* The caller is required to pass record parms--ensure they are valid */
  1381.  
  1382.   pRecordParms= ( PMCI_RECORD_PARMS )pFuncBlock->ulParam2;
  1383.  
  1384.   ulrc = CheckMem ( (PVOID) pRecordParms,
  1385.                     sizeof (MCI_RECORD_PARMS), PAG_READ);
  1386.  
  1387.  
  1388.   if (ulrc != MCIERR_SUCCESS)
  1389.      {
  1390.      return MCIERR_MISSING_PARAMETER;
  1391.      }
  1392.  
  1393.  
  1394. //  if ( ulpInstance->fCanRecord & DEVICE_CAP_UNKNOWN )
  1395. //     {
  1396. //     /* init to 0 to save an if later */
  1397. //     MCI_GETDEVCAPS_PARMS  mcap;
  1398. //
  1399. //     ulpInstance->fCanRecord = FALSE;
  1400. //     mcap.ulItem = MCI_GETDEVCAPS_CAN_RECORD;
  1401. //
  1402. //     ulrc = mciSendCommand ( ulpInstance->usAmpDeviceID,
  1403. //                             MCI_GETDEVCAPS,
  1404. //                             MCI_GETDEVCAPS_ITEM | MCI_WAIT,
  1405. //                             (PVOID) &mcap,
  1406. //                             pFuncBlock->usUserParm);
  1407. //
  1408. //     if ( ( ulrc & 0xffff) == MCIERR_SUCCESS )
  1409. //        {
  1410. //        ulpInstance->fCanRecord = mcap.ulReturn;
  1411. //        }
  1412. //     }
  1413.  
  1414.   /******************************************
  1415.   * If the io proc that we are currently
  1416.   * using does not support writing, return
  1417.   * an error (OpenFile shows how to determine
  1418.   * these capabilities).
  1419.   *******************************************/
  1420.  
  1421. // CONNECTION FEATURE
  1422. // 6421--Need to know if the HARDWARE can record--however, this info
  1423. // really is in the VSD--since the VSD is now off limits--how do I
  1424. // access it?????
  1425. //
  1426. // 1. Add a new flag to MCI_GETDEVCAPS saying another MCI driver is
  1427. // making the call??---barf :-(
  1428. // 2. Add new device specific parms (possible--but UGLY!!!!, this info
  1429. // is already in the resource file but I can't get at it!!!
  1430.  
  1431.  
  1432. // 6421--must receive device caps from mci_init structure
  1433.  
  1434. // should I use test connection in record mode?
  1435.  
  1436. // CONNECTION FEATURE --CanRecord is still undefined
  1437.  
  1438.   if ( ( !( ulpInstance->ulCapabilities & CAN_RECORD )  &&
  1439.          !ulpInstance->usPlayLstStrm ) ||
  1440.          CanPlayRecord( ulpInstance, MCIDRV_INPUT ) )
  1441.      {
  1442.      return ( MCIERR_UNSUPPORTED_FUNCTION );
  1443.      }
  1444.  
  1445.   if ( ulpInstance->usPlayLstStrm )
  1446.      {
  1447.      return ( MCIERR_SUCCESS );
  1448.      }
  1449.  
  1450.  
  1451.   ConvertTimeUnits ( ulpInstance,
  1452.                      &ulMMFileLength,
  1453.                      FILE_LENGTH);
  1454.  
  1455.   ulTemp1 = ulMMFileLength;
  1456.  
  1457.   /* what is this statement for? */
  1458.  
  1459.   ConvertToMM( ulpInstance, &ulMMFileLength, ulMMFileLength );
  1460.  
  1461.  
  1462.    /***********************************************
  1463.    * Unlike play processing, checking to see if
  1464.    * MCI_FROM on a record is valid is easy,
  1465.    * one just has to check to see if the FROM is
  1466.    * a valid position in the file.
  1467.    ***********************************************/
  1468.  
  1469.    if (ulParam1 & MCI_FROM)
  1470.       {
  1471.       /*********************************************
  1472.       * We cannot start recording from a position
  1473.       * which is greater than the length of the
  1474.       * file.
  1475.       *********************************************/
  1476.       // add code to avoid playlist problems.
  1477.  
  1478.       if ( pRecordParms->ulFrom > ulTemp1 )
  1479.         {
  1480.         return ( MCIERR_OUTOFRANGE );
  1481.         }
  1482.  
  1483.       /***********************************************
  1484.       * The record will start from the from position
  1485.       * If MCI_FROM is not specified, then the routines
  1486.       * below will assume the current file position for
  1487.       * the from.  Either way, convert the from position
  1488.       * to MM time and store it.
  1489.       ************************************************/
  1490.  
  1491.       ulrc = ConvertToMM ( ulpInstance,
  1492.                            (ULONG*) &ulFromPosition,
  1493.                            pRecordParms->ulFrom );
  1494.  
  1495.       }  /* Record From */
  1496.  
  1497.    /*****************************************************
  1498.    * If the MCI_TO flag is specified on a record, then
  1499.    * the following checks are required to ensure that
  1500.    * the to position is valid.
  1501.    *
  1502.    * A. The to position must be greater than 0.
  1503.    * B. If the from flag is specified, the to position
  1504.    *    must be greater than the from position.
  1505.    * C. If a stream has been created, then
  1506.    *    I. Ensure that the to position is not less than
  1507.    *       our current stream position.
  1508.    ******************************************************/
  1509.  
  1510.    if (ulParam1 & MCI_TO)
  1511.       {
  1512.       /* The to position must be greater than 0 */
  1513.  
  1514.       if ( pRecordParms->ulTo <= 0 )
  1515.         {
  1516.         return ( MCIERR_OUTOFRANGE );
  1517.         }
  1518.  
  1519.       if ( ulParam1 & MCI_FROM )
  1520.         {
  1521.  
  1522.         /* Recording behind the from position is illegal */
  1523.  
  1524.         if ( pRecordParms->ulTo <= pRecordParms->ulFrom )
  1525.            {
  1526.            return ( MCIERR_OUTOFRANGE );
  1527.            }
  1528.  
  1529.         } /* if the from flag specified */
  1530.  
  1531.       /* Check to see if a stream has been created */
  1532.  
  1533.       if ( ulpInstance->ulCreateFlag == PREROLL_STATE )
  1534.          {
  1535.  
  1536.          /**********************************************
  1537.          * Get the current stream time, it will be used
  1538.          * for range checking below.
  1539.          ***********************************************/
  1540.  
  1541.          ulrc = SpiGetTime( ulpInstance->StreamInfo.hStream,
  1542.                             ( PMMTIME ) &( ulpInstance->StreamInfo.mmStreamTime ) );
  1543.  
  1544.          if ( ulrc )
  1545.             {
  1546.             return ( ulrc );
  1547.             }
  1548.  
  1549.          /* Since the stream time is in mm time, convert the to point to MM time */
  1550.  
  1551.          ConvertToMM ( ulpInstance,
  1552.                        (ULONG*) &ulTempTO,
  1553.                        pRecordParms->ulTo );
  1554.  
  1555.          /*********************************************************
  1556.          * The documentation states that the record will start from
  1557.          * either the current position or the from point.
  1558.          * If the from flag was not passed in, then set the
  1559.          * from position to be equivalent to our current position
  1560.          * for simplicity purposes.
  1561.          * Note: ulFromPosition was filled in the from processing
  1562.          ********************************************************/
  1563.  
  1564.          if ( ulFromPosition == 0 &&
  1565.               !(ulParam1 & MCI_FROM ) )
  1566.             {
  1567.             ulFromPosition = ( ULONG ) ulpInstance->StreamInfo.mmStreamTime;
  1568.             }
  1569.  
  1570.          if ( ulTempTO <= ulpInstance->StreamInfo.mmStreamTime &&
  1571.               ulTempTO <= ulFromPosition         )
  1572.             {
  1573.  
  1574.             /********************************************************
  1575.             * it is possible that we had rounding problems so ensure
  1576.             * that user did indeed pass in an illegal value
  1577.             ********************************************************/
  1578.             if ( ( ulParam1 & MCI_FROM ) &&
  1579.                  !(pRecordParms->ulTo <= pRecordParms->ulFrom ) )
  1580.                {
  1581.  
  1582.                }
  1583.             else
  1584.                {
  1585.                return ( MCIERR_OUTOFRANGE );
  1586.                }
  1587.             }
  1588.  
  1589.          } /* if stream has been created */
  1590.  
  1591.       } /* MCI_TO was specified */
  1592.  
  1593.    /*******************************************************
  1594.    * Playlists to not support the record insert message to
  1595.    * io procs--so don't do it.
  1596.    *******************************************************/
  1597.  
  1598.    if (pFuncBlock->ulParam1 & MCI_RECORD_INSERT)
  1599.       {
  1600.       ulpInstance->fRecdInsert = TRUE;
  1601.       }   /* Insert Specified */
  1602.  
  1603.   return (MCIERR_SUCCESS);
  1604.  
  1605. } /* Check Record Flags */
  1606.  
  1607.  
  1608.