/********START OF SPECIFICATulIONS *********************
*
* SUBROUTINE NAME: MCIConnection
*
* DESCRIPTIVE NAME: Audio MCD Connection Routine
*
*              Copyright (c) IBM Corporation  1991, 1993
*                        All Rights Reserved
*
* FUNCTION: Processes connection messages from MCI drivers and applications.
*
* NOTES:
*
* ENTRY POINTS:
*
* INPUT: MCI_STATUS message.
*
* EXIT-NORMAL:Lo Word Return  Code MCIERR_SUCCESS
*
* EXIT_ERROR:  Error Code.
*
* EFFECTS:
*
*
* INTERNAL REFERENCES:
*                        ConvertTimeUnits ().
*                        ConVertToMM
*                        SetAudioDevice().
*                        SetWaveDeviceDefaults().
*                        CheckMem ().
*
* EXTERNAL REFERENCES:
*                        SpiGetTime       ()        - MME API
*                        mciSendCommand   ()        - MME API
*
*********************** END OF SPECIFICATIONS **********************/
#define INCL_BASE                       // Base Dos APIs.
#define INCL_ERRORS                     // All the errors.
#define INCL_MMIO_CODEC


#include <os2.h>                        // OS2 includes.
#include <string.h>                     // String Functions
#include <math.h>                       // Math Functions
#include <os2medef.h>                   // MME includes files.
#include <ssm.h>                        // SSM spi includes.
#include <meerror.h>                    // MM Error Messages.
#include <mmioos2.h>                    // MMIO Include.
#include <mcios2.h>                     // MM System Include.
#include <mmdrvos2.h>                   // Mci Driver include.
#include <mcd.h>                        // AUDIO IF DriverInterface.
#include <hhpheap.h>                    // Heap Manager Definitions.
#include <qos.h>
#include <audiomcd.h>                   // Component Definitions.
#include <admcfunc.h>                   // Function Prototypes
#include <checkmem.h>
#include <mcipriv.h>
#include <filter.h>

#ifdef CONNECTION
/********************* START OF SPECIFICATIONS *********************
*
* SUBROUTINE NAME: MCIConnection
*
* FUNCTION: Process MCI_CONNECTION message.
*
* NOTES: This function can make an MCI or SSM connection.
*        it also can break an MCI or SSM connection.
*
* ENTRY POINTS:
*
* INPUT: FUNCTION_PARM_BLOCK
*
* EXIT-NORMAL: MCIERR_SUCCESS
*
* EXIT_ERROR:
*
* EFFECTS:
*
* EXTERNAL REFERENCES: mmioSendMessage()    - MMIO function
*
*********************** END OF SPECIFICATIONS **********************/
ULONG MCIMakeConnection ( FUNCTION_PARM_BLOCK *pFuncBlock)
{
  ULONG                ulrc = MCIERR_SUCCESS;    // Error Value
//  ULONG                ulConnFlags;
  ULONG                ulParam1 = pFuncBlock->ulParam1;       // Msg Flags

  INSTANCE             *pInstance;   // Local Instance

  PCONCB               pconcb = (PCONCB) pFuncBlock->ulParam2;

//  ulConnFlags = ulParam1 = pFuncBlock->ulParam1;

  pInstance= (INSTANCE *) pFuncBlock->ulpInstance;
  ulParam1 &= ~(MCI_WAIT | MCI_NOTIFY );

   /*-------------------------------------
   * If the caller wants to specify a
   * connector type and its not digital
   * wave stream connection --error out.
   *------------------------------------*/

   if ( pconcb->ulConnectorType != MCI_WAVE_STREAM_IN_CONNECTOR &&
        pconcb->ulConnectorType != MCI_WAVE_STREAM_OUT_CONNECTOR )
      {

      return ( MCIERR_INVALID_CONNECTOR_TYPE );
      }

   /*-------------------------------------
   * If the MCI driver is already connected
   * (the wave driver only supports one
   * connections) return an error.
   *------------------------------------*/

   if ( pInstance->fConnected )
      {
      return ( MCIERR_ALREADY_CONNECTED );
      }

// what about Index?????

   if ( ulParam1 == MCIDRV_CONNECT_TARGET )
      {
      PMMAUDIOHEADER pAudHeader = (PMMAUDIOHEADER) pconcb->pMediaHeader;

      if ( !pAudHeader ||
           pAudHeader->ulMediaType != MMIO_MEDIATYPE_AUDIO )
         {
         return (MCIERR_INVALID_MEDIA_TYPE );
         }

      // copy the header for future reference

      memmove ( (PVOID) &pInstance->mmAudioHeader, pAudHeader, sizeof( MMAUDIOHEADER ) );

      pInstance->ulAverageBytesPerSec = pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec;
      if ( pInstance->ulAverageBytesPerSec == 0 )
         {
         // LAD--this code may still trap.

         pInstance->ulAverageBytesPerSec = pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec;
          pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec =
          ( ULONG ) ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec *
                      pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usBitsPerSample *
                      pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usChannels ) / 8;

         }


      pconcb->hid = pInstance->StreamInfo.hidATarget;
      pconcb->pevfn = (PEVFN)RecordEventRoutine;
      pInstance->ulOperation = MCIDRV_INPUT;
      }
   else
      {
      pconcb->hid = pInstance->StreamInfo.hidASource;
      pconcb->pevfn = (PEVFN) PlayEventRoutine;
      pInstance->ulOperation = MCIDRV_OUTPUT;
      }

   /* --------------------------------------------
   * Fill in the remainder of the CONCB struct
   * this includes:
   *    DCB (Device Control Block )
   *    SPCBKey (stream identifier )
   *    Event Routine (where spi events come )
   *    Event Control Block -- memory for events
   *----------------------------------------------*/

   pconcb->pdcb = NULL;




   if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usBitsPerSample == 8 )
      {
      if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usFormatTag == DATATYPE_MULAW ||
           pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usFormatTag == DATATYPE_RIFF_MULAW ||
           pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usFormatTag == DATATYPE_ALAW       ||
           pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usFormatTag == DATATYPE_RIFF_ALAW)
         {
         if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usChannels == 1)
            {                                      // 8-bit mono
            if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 8000)
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B8KM;
               }
            else if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 11025)
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B11KM;
               }
            else if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 22050)
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B22KM;
               }
            else
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B44KM;
               }
            }
         else                                       // 8bit, stereo
            {
            if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 8000)
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B8KS;
               }
            else if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 11025)
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B11KS;
               }
            else if (pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 22050)
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B22KS;
               }
            else
               {
               pInstance->StreamInfo.SpcbKey.ulDataSubType = MULAW_8B44KS;
               }
            }

         } // if mulaw



      if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usChannels == 1 )
         {
         if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 8000 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_8M08;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 11025 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_1M08;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 22050 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_2M08;
            }
         else
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_4M08;
            }
         } /* if this is a mono file */
      else
         {
         if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 8000 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_8S08;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 11025 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_1S08;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 22050 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_2S08;
            }
         else
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_4S08;
            }
         } /* If this is a stereo file */
      } /* if this is an 8-bit file */
   else
      {
      if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usChannels == 1 )
         {
         if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 8000 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_8M16;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 11025 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_1M16;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 22050 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_2M16;
            }
         else
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_4M16;
            }
         } /* if this is a mono file */
      else
         {
         if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 8000 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_8S16;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 11025 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_1S16;
            }
         else if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec <= 22050 )
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_2S16;
            }
         else
            {
            pInstance->StreamInfo.SpcbKey.ulDataSubType = WAVE_FORMAT_4S16;
            }
         } /* If this is a stereo file */
      } /* this is a 16-bit file */

   // ensure that spcbkey is copied AFTER we fill in the relevant
   // information.

   memmove( &pconcb->spcbkey, &pInstance->StreamInfo.SpcbKey, sizeof( SPCBKEY) );
   pInstance->StreamInfo.SpcbKey.ulDataType = DATATYPE_WAVEFORM; // &pInstance->StreamInfo.SpcbKey;


#ifdef MMHACK
   // considerable hack to work around fact that there isn't a RIFF-alaw/mulaw
   // spcbkey, just the IBM alaw/mulaw.  These datatypes really are identical.

   if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usFormatTag == DATATYPE_RIFF_MULAW )
      {
      pconcb->spcbkey.ulDataType = DATATYPE_MULAW;
      }
   if ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usFormatTag == DATATYPE_RIFF_ALAW)
      {
      pconcb->spcbkey.ulDataType = DATATYPE_ALAW;
      }
#endif

   pconcb->pMediaHeader = (PVOID) &pInstance->mmAudioHeader;

   pconcb->pevcb = (PEVCB) &pInstance->StreamInfo.Evcb;
//   pconcb->phevent = &pInstance->StreamInfo.hEvent;


   /* Set state variable to indicate conneciton has been made */

   pInstance->fConnected = TRUE;

//  pInstance->ulFlags &= ~HAVE_SPCBKEY;
  pInstance->pconcb = pconcb;

  return ( ulrc );

} /* MCIConnection */
#endif


/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: BreakMCIConnection
*
* DESCRIPTIVE NAME: This function will break the connection to whatever
*                   device is connected to the audio MCD driver.  It will also
*                   destroy any streams between the two connections.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG MCIBreakConnection( FUNCTION_PARM_BLOCK *pFuncBlock)

{
   INSTANCE         *pInstance;   // Local Instance
   ULONG            ulAbortNotify = FALSE; // whether to abort play/record operation

   pInstance= (INSTANCE *) pFuncBlock->ulpInstance;


   /*-------------------------------------
   * If the MCI driver is already connected
   * (the wave driver only supports one
   * connections) return an error.
   *------------------------------------*/

   if ( !pInstance->fConnected )
      {
      return ( MCIERR_NO_CONNECTION );
      }


  /****************************************
  * If There are any Pending Notifies
  * Post Abort Message for Those Operations
  *****************************************/

  GetNotifyAbortAccess( pInstance, &ulAbortNotify );

  if ( ulAbortNotify == TRUE)
     {

     /* Stop the command on another thread */
     pFuncBlock->usMessage = MCI_CLOSE;
     GenericThreadAbort( pInstance, pFuncBlock, pFuncBlock->ulParam1 );

     } /* Pending Notifies */



   pInstance->fConnected = FALSE;

   return ( MCIERR_SUCCESS );

} /* MCIBreakConnection */

#ifdef CONNECTION
/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: MCITestConnection
*
* DESCRIPTIVE NAME: This function will determine if a connection can be made.
*                   Caller must be using the wave stream connector and also pass
*                   in a valid media header (i.e. MMAUDIOHEADER ).
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG MCIModifyConnection( FUNCTION_PARM_BLOCK *pFuncBlock)

{
ULONG            ulConnFlags;

INSTANCE         *pInstance;   // Local Instance

PCONCB           pconcb = (PCONCB) pFuncBlock->ulParam2;
PMMAUDIOHEADER   pAudHeader = pconcb->pMediaHeader;

  ulConnFlags = pFuncBlock->ulParam1;

  pInstance= (INSTANCE *) pFuncBlock->ulpInstance;

   /*-------------------------------------
   * If the caller wants to specify a
   * connector type and its not digital
   * wave stream connection --error out.
   *------------------------------------*/

   if ( pconcb->ulConnectorType != MCI_WAVE_STREAM_IN_CONNECTOR &&
        pconcb->ulConnectorType != MCI_WAVE_STREAM_OUT_CONNECTOR )
      {
      return ( MCIERR_INVALID_CONNECTOR_TYPE );
      }

   if ( !pAudHeader ||
        pAudHeader->ulMediaType != MMIO_MEDIATYPE_AUDIO )
      {
      return (MCIERR_INVALID_MEDIA_TYPE );
      }
   /* The caller only wishes to see if we can support this mode */

   if ( !(ulConnFlags & MCIDRV_TEST) )
      {
      // copy the header for future reference

//      memmove ( (PVOID) &pInstance->mmAudioHeader, pAudHeader, sizeof( MMAUDIOHEADER ) );

//      pInstance->ulAverageBytesPerSec = pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec;
//      if ( pInstance->ulAverageBytesPerSec == 0 )
//         {
         // LAD--this code may still trap.

//         pInstance->ulAverageBytesPerSec = pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec;
//          pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec =
//          ( ULONG ) ( pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec *
//                      pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usBitsPerSample *
//                      pInstance->mmAudioHeader.mmXWAVHeader.WAVEHeader.usChannels ) / 8;

//         }
      } /* else we are truly modifying the audio header */


   return ( MCIERR_SUCCESS );

} /* MCITestConnection */
#endif

/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: MCIAssociate.
*
* DESCRIPTIVE NAME: This function will be called by MDM to associate any
*                   audio specific information to our stream handler (usually
*                   the file system stream handler).
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG MCIAssociate ( FUNCTION_PARM_BLOCK *pFuncBlock)

{
  ULONG                ulrc;           // Error Value
  ULONG                ulConnFlags;

  INSTANCE             *pInstance;   // Local Instance

  PCONCB               pconcb = (PCONCB) pFuncBlock->ulParam2;
  extern HID           hidASource;
  extern HID           hidATarget;


  ulConnFlags = pFuncBlock->ulParam1;

  pInstance= (INSTANCE *) pFuncBlock->ulpInstance;

  /*-----------------------------------------------------
  * Indicate that we know that a stream has been created
  * and store the stream handle for stream destruction.
  *----------------------------------------------------*/
  pInstance->StreamInfo.hStream = pconcb->hstream;

  /* Save the fact that we know the stream is created */
  /***************************
  * Set a flag which indicates
  * that a stream has been
  * created.
  ****************************/
  pInstance->ulCreateFlag = PREROLL_STATE;

  /* Perform associations if necessary */

  pInstance->ulOperation = ulConnFlags & ( MCIDRV_INPUT | MCIDRV_OUTPUT );
  if ( pInstance->usPlayLstStrm )
     {
     ulrc = AssociatePlayList ( pInstance );
     }
  else
     {
     /**********************************************************
     * The stream must be associated with a data object
     * in our case a mmio file handle.  The file system
     * stream handler (FSSH) will always be the stream handler
     * that we want to associate the data object with,
     * therefore, if we have created a playback stream then
     * FSSH is the source, so associate with the source.  On a
     * record stream, FSSH is the target, so associate with
     * the target.
     **********************************************************/


     pInstance->StreamInfo.acbmmio.ulObjType = ACBTYPE_MMIO;
     pInstance->StreamInfo.acbmmio.ulACBLen = sizeof (ACB_MMIO);
     pInstance->StreamInfo.acbmmio.hmmio = pInstance->hmmio;
     if ( ulConnFlags & MCIDRV_OUTPUT )
         {

         ulrc = ADMCAssociate ( pconcb->hstream,
                               hidASource,
                               (PVOID) &pInstance->StreamInfo.acbmmio);


         }  /* Associating play stream */

     else // if ( pInstance->ulOperation == MCIDRV_INPUT )
         {
         ulrc = ADMCAssociate ( pconcb->hstream,
                                hidATarget,
                                (PVOID) &pInstance->StreamInfo.acbmmio);

         } /* Associating record stream */

     } /* else this is not a playlist */

  return ( ulrc );

} /* MCIAssociate */

/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: TestNetwork
*
* DESCRIPTIVE NAME: This function queries MDM to determine if the network
*                   we are attached to can record.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG TestNetwork( INSTANCE                    *pInstance,
                   PMCI_WAVE_GETDEVCAPS_PARMS  pExtDevCaps )

{
   CONCB         concb;
   MMAUDIOHEADER mmaudioheader;
   ULONG         ulDirection;
   ULONG         ulrc;

   if ( !pInstance->fConnected )
      {
      return (MCIERR_NO_CONNECTION );
      }

//  pExtDevCaps->ulBitsPerSample;


  mmaudioheader.mmXWAVHeader.WAVEHeader.usFormatTag       =  pExtDevCaps->ulFormatTag;
  mmaudioheader.mmXWAVHeader.WAVEHeader.usChannels        =  pExtDevCaps->ulChannels ;
  mmaudioheader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec   =  pExtDevCaps->ulSamplesPerSec;

  //  If mmaudioheader contains !0, then assume source, else must be target.

  if ( pExtDevCaps->ulFormatMode == MCI_PLAY )
     {
     mmaudioheader.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes = 1;
     }
  else
     {
     mmaudioheader.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes = 0;
     }


//  concb.ulConnectorIndex = 1;
  concb.ulConnectorType = pInstance->pconcb->ulConnectorType;
  concb.ulConnectorIndex = pInstance->pconcb->ulConnectorIndex;
  mmaudioheader.mmXWAVHeader.WAVEHeader.usBitsPerSample   =  pExtDevCaps->ulBitsPerSample;
  mmaudioheader.ulMediaType = MMIO_MEDIATYPE_AUDIO;
  mmaudioheader.ulHeaderLength = sizeof( MMAUDIOHEADER );

   concb.pMediaHeader = ( PVOID) &mmaudioheader;
#ifndef CONNECTION
   ulrc = mciSendCommand ( pInstance->usAmpDeviceID,
#else
   ulrc = mciSendCommand ( pInstance->usWaveDeviceID,
#endif
                           MCIDRV_MODIFY_CONNECTION,
                           MCIDRV_TEST | pInstance->ulOperation, // operation contains direction
                           (PVOID) &concb,
                           0 );


   return ( ulrc );

} /* TestNetwork */



/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: CanPlayRecord
*
* DESCRIPTIVE NAME: This function queries MDM to determine if the network
*                   can either produce/consume information.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG CanPlayRecord( INSTANCE   *pInstance,
                     ULONG      ulDirection )

{
   CONCB   concb;
   ULONG   ulrc;


   if ( !pInstance->fConnected )
      {
      return (MCIERR_NO_CONNECTION );
      }

   concb.ulConnectorType = pInstance->pconcb->ulConnectorType;
   concb.ulConnectorIndex = pInstance->pconcb->ulConnectorIndex;

   concb.pMediaHeader = ( PVOID) &pInstance->mmAudioHeader;
   pInstance->mmAudioHeader.ulMediaType = MMIO_MEDIATYPE_AUDIO;

#ifndef CONNECTION
   ulrc = mciSendCommand ( pInstance->usAmpDeviceID,
#else
   ulrc = mciSendCommand ( pInstance->usWaveDeviceID,
#endif
                           MCIDRV_MODIFY_CONNECTION,
                           MCIDRV_TEST | ulDirection,
                           (PVOID) &concb,
                           0 );

   return ( ulrc );

} /* CanPlayRecord */

/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: ModifyConnection
*
* DESCRIPTIVE NAME: This function queries MDM to determine if the network
*                   can either produce/consume information.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG ModifyConnection( INSTANCE   *pInstance )

{

extern HID   hidBSource;
extern HID   hidBTarget;


   CONCB   concb;
   ULONG   ulrc;

   if ( !pInstance->fConnected )
      {
      return (MCIERR_SUCCESS );
      }

   pInstance->mmAudioHeader.ulMediaType = MMIO_MEDIATYPE_AUDIO;


#ifndef CONNECTION
   concb.pMediaHeader = ( PVOID) &pInstance->mmAudioHeader;
   concb.ulFlags = VSD_SET_ALL;

   ulrc = mciSendCommand ( pInstance->usAmpDeviceID,
#else
   memmove( &concb, pInstance->pconcb, sizeof( CONCB ) );
   concb.pMediaHeader = ( PVOID) &pInstance->mmAudioHeader;

   if ( pInstance->ulOperation == MCIDRV_INPUT )
      {
       concb.hid = pInstance->StreamInfo.hidATarget;
      }
   else
      {
      concb.hid = pInstance->StreamInfo.hidASource;
      }


   // wave device ALWAYS uses the FIRST wave stream connector
   // if we are outputing data, then use the outpu connector,
   // else we are using the input connector to receive data.

//   if ( pInstance->ulOperation == MCIDRV_OUTPUT )
//      {
//      concb.ulConnectorType = MCI_WAVE_STREAM_OUT_CONNECTOR;
//      }
//   else
//      {
//      concb.ulConnectorType = MCI_WAVE_STREAM_IN_CONNECTOR;
//      }

//   concb.ulConnectorIndex = 1;
   concb.ulConnectorType = pInstance->pconcb->ulConnectorType;
   concb.ulConnectorIndex = pInstance->pconcb->ulConnectorIndex;
   ulrc = mciSendCommand ( pInstance->usWaveDeviceID,
#endif
                           MCIDRV_MODIFY_CONNECTION,
                           pInstance->ulOperation,
                           (PVOID) &concb,
                           0 );

   if ( ulrc )
     return ( ULONG_LOWD(ulrc) );


#ifndef CONNECTION
   pInstance->StreamInfo.SpcbKey.ulDataType    = concb.spcbkey.ulDataType;
   pInstance->StreamInfo.SpcbKey.ulDataSubType = concb.spcbkey.ulDataSubType;
   pInstance->StreamInfo.SpcbKey.ulIntKey = 0;

   /*------------------------------------------------------
   * Get certain streaming information from the stream handler
   * we have loaded.
   *--------------------------------------------------------*/
   ulrc = SpiGetProtocol( concb.hid,
                          &pInstance->StreamInfo.SpcbKey,
                          &pInstance->StreamInfo.spcb );

   if ( ulrc )
      {
      return ( ulrc );
      }


   pInstance->ulBytes   = pInstance->StreamInfo.spcb.ulBytesPerUnit;
   pInstance->ulMMTime  = pInstance->StreamInfo.spcb.mmtimePerUnit;

   /* Save the hid from the connected device--use for spcbkeys etc. */

   if ( pInstance->ulOperation == MCIDRV_INPUT )
      {
      hidBSource = concb.hid;
      }
   else
      {
      hidBTarget = concb.hid;
      }

   memmove( &pInstance->StreamInfo.SpcbKey, &concb.spcbkey, sizeof( SPCBKEY) );
   pInstance->pdcb = concb.pdcb;

#else
//  pInstance->ulFlags &= ~HAVE_SPCBKEY;
#endif
   return ( ulrc );

} /* ModifyConnection */


/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: FindAmp
*
* DESCRIPTIVE NAME: Gets the device ID of the first Amplifier/Mixer in the network
*                   All audio commands (such as volume, treble, bass etc.)
*                   we can't process will go there.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/
#ifdef CONNECTION
ULONG FindAmp( INSTANCE *pInstance )

{
   MCIDRV_GETDEVICETYPE_ID_PARMS   idparms;

   ULONG   ulrc = MCIERR_SUCCESS;

   if ( !pInstance->fConnected )
      {
      return (MCIERR_NO_CONNECTION );
      }

   if ( pInstance->fFoundMixer )
      {
      return ( MCIERR_SUCCESS );
      }

   /* Tell system we want the first mixer */
#ifndef CONNECTION
   pInstance->fFoundMixer = TRUE;
   ulrc = MCIERR_SUCCESS;  // temp hack
#else
   idparms.ulDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
   ulrc = mciSendCommand ( pInstance->usWaveDeviceID,
                           MCIDRV_GETDEVICETYPE_ID,
                           MCI_WAIT,
                           (PVOID) &idparms,
                           0 );

   if ( !ulrc )
      {
      pInstance->usAmpDeviceID = idparms.ulDeviceID;
      pInstance->fFoundMixer = TRUE;
      }


#endif
   /* If we found a mixer, then these commands can be safely routed */

   return ( ulrc );

} /* FindAmp */
#endif

/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: CheckForConnection
*
* DESCRIPTIVE NAME: Determines if MCD is connected to another MCD.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/

ULONG CheckForConnection( INSTANCE *pInstance )

{
   if ( !pInstance->fConnected )
      {
      return (MCIERR_NO_CONNECTION );
      }

   return ( MCIERR_SUCCESS );

} /* CheckForConnection */


/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: GetStreamingSemaphore
*
* DESCRIPTIVE NAME: This function queries to acquire the streaming semaphore
*                   for cross MCD control.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/
ULONG GetStreamingSemaphore( INSTANCE *pInstance )

{
// return real semaphore when defined
return ( MCIERR_SUCCESS );
}

/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: ReleaseStreamingSemaphore
*
* DESCRIPTIVE NAME: This function releases the streaming semaphore
*                   for cross MCD control.
*
* FUNCTION:
*
*************************** END OF SPECIFICATIONS *************************/
ULONG ReleaseStreamingSemaphore( INSTANCE *pInstance )

{
// return real semaphore when defined
return ( MCIERR_SUCCESS );
}



/************************ START OF SPECIFICATIONS **************************
*
* SUBROUTINE NAME: StartPlayBack
*
* DESCRIPTIVE NAME: This function starts a streaming network.
*
* FUNCTION:
*
* ulStartFlags : MCIDRV_CUE_PLAYBACK
*                MCIDRV_CUE_RECORD
*                MCIDRV_START_PLAYBACK
*                MCIDRV_START_RECORD
*
*************************** END OF SPECIFICATIONS *************************/
ULONG StartStream( INSTANCE *pInstance,
                   ULONG    ulStartFlags )

{
//CONCB   concb;
ULONG   ulrc;
ULONG   ulSpiFlags = SPI_START_STREAM;
CONCB   concb;
#ifndef CONNECTION

   if ( ulStartFlags == MCIDRV_CUE_PLAYBACK )
      {
      ulSpiFlags = SPI_START_PREROLL;
      }
   ulrc = SpiStartStream(pInstance->StreamInfo.hStream, ulSpiFlags );
#else

  concb.pMediaHeader = ( PVOID ) &pInstance->mmAudioHeader;
  pInstance->mmAudioHeader.ulMediaType = MMIO_MEDIATYPE_AUDIO;

  ulrc = mciSendCommand ( pInstance->usWaveDeviceID,
          MCIDRV_START,
          ulStartFlags,
          &concb,
          0 );

#endif
  return ( ulrc );
}
