/****************************************************************************/
/*                                                                          */
/* SOURCE FILE NAME:  CDAUDUTL.C                                            */
/*                                                                          */
/* DESCRIPTIVE NAME:  CD AUDIO MCI DRIVER UTILITIES                         */
/*                                                                          */
/* COPYRIGHT:  (c) IBM Corp. 1991 - 1993                                    */
/*                                                                          */
/* FUNCTION:  This file contains the hardware independent code that         */
/*            supplement the PROCESS commands in CDAUDPRO.C for the         */
/*            CD Audio MCI Driver uses.  The entry point to the DLL is      */
/*            in CDAUDIO.C                                                  */
/*                                                                          */
/* NOTES:  The hardware dependent code is found in file IBMCDROM.C.         */
/*                                                                          */
/* OTHER FUNCTIONS:                                                         */
/*       SetAudio      - Set audio information from MCI_SET.                */
/*       SetConnector  - Enable or disable connection.                      */
/*       SetCuePoint   - Enable cue point.                                  */
/*       StatusMCD     - Get status from MCD information.                   */
/*       StatusMCDDef  - Get status from MCD default information.           */
/*       StatusVSD     - Get status from VSD information.                   */
/*       DisableEvents - Disable cuepoints and position advise.             */
/*       GetTimeAddr   - Convert a time format to/from MMTIME.              */
/*       GetTimeAddrRC - Colinize return code to time format.               */
/*       GetTrackInfo  - Get Track info for specified track or address.     */
/*       ValAddress    - Validate address for play and seek commands.       */
/*       ValState      - Validate state, has disc and not suspended.        */
/*       vsdResponse   - Process VSD Response.                              */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

#define INCL_DOSERRORS
#define INCL_DOSPROCESS
#define INCL_DOSMEMMGR
#define INCL_DOSMODULEMGR
#define INCL_DOSSEMAPHORES
#include <os2.h>
#include <string.h>
#define INCL_MCIOS2
#include <os2me.h>
#include <cdaudos2.h>
#include "cdaudibm.h"
#include <ctype.h>


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  SetAudio                                               */
/*                                                                          */
/* DESCRIPTIVE NAME:  Set Audio                                             */
/*                                                                          */
/* FUNCTION:  Set Audio information when MCI_SET_AUDIO flag is used with    */
/*            MCI_SET command.  This function is called by ProcSet().       */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst      -- pointer to instance.                            */
/*      ULONG *pulParam1 -- flag for this message.                          */
/*      MCI_SET_PARMS *pParam2 -- pointer to local SET structure.           */
/*      ULONG *pulMode   -- mode flags.                                     */
/*      ULONG *pulLevel  -- volume level.                                   */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_FLAGS_NOT_COMPATIBLE -- Flags not compatible.                */
/*      MCIERR_INVALID_AUDIO_FLAG   -- Unknown audio flag specified.        */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/****************************************************************************/

ULONG SetAudio(PINST pInst, ULONG *pulParam1, MCI_SET_PARMS *pParam2,
               ULONG *pulMode, ULONG *pulLevel)
{
   ULONG rc = MCIERR_SUCCESS;          // assume the best
   USHORT usFlag = 0;
   ULONG  ulChan = 0;
   USHORT left, right;

   if (*pulParam1 & MCI_SET_ON && *pulParam1 & MCI_SET_OFF)
      rc = MCIERR_FLAGS_NOT_COMPATIBLE;
   else
      switch (pParam2->ulAudio)
      {
         case MCI_SET_AUDIO_LEFT :
            ulChan = CDMC_LFT_CH;
            break;
         case MCI_SET_AUDIO_RIGHT :
            ulChan = CDMC_RGT_CH;
            break;
         case MCI_SET_AUDIO_ALL :
            ulChan = CDMC_ALL_CH;
            break;
         default : rc = MCIERR_INVALID_AUDIO_FLAG;
      }  /* of switch */


   if (!rc)
   {
      *pulParam1 ^= MCI_SET_AUDIO;       // remove non VSD flag

      /* Remove flag for vectored volume, not supported in this example */
      if (*pulParam1 & MCI_OVER)
         *pulParam1 ^= MCI_OVER;

      if (*pulParam1 & MCI_SET_ON)
      {
         usFlag = 1;
         *pulParam1 ^= MCI_SET_ON;       // remove non VSD flag
         *pulMode |= ulChan;
      }  /* of else if ON */
      else if (*pulParam1 & MCI_SET_OFF)
      {
         usFlag = 1;
         *pulParam1 ^= MCI_SET_OFF;      // remove non VSD flag
         if (ulChan & CDMC_LFT_CH)
            *pulMode &= CHAN_MODE_SET | CDMC_RGT_CH;
         if (ulChan & CDMC_RGT_CH)
            *pulMode &= CHAN_MODE_SET | CDMC_LFT_CH;
      }  /* of else if OFF */

      if (*pulParam1 & MCI_SET_VOLUME)
      {
         usFlag = 1;
         /* get requested volume levels */
         left  = (USHORT) pParam2->ulLevel;
         right = (USHORT) pParam2->ulLevel;

         if (ulChan & CDMC_LFT_CH)
            if (left > 100)
               VOL_LEFT(*pulLevel) = 100;
            else
               VOL_LEFT(*pulLevel) = left;
         if (ulChan & CDMC_RGT_CH)
            if (right > 100)
               VOL_RIGHT(*pulLevel) = 100;
            else
               VOL_RIGHT(*pulLevel) = right;
      }  /* of setting volume */

      if (usFlag)                // CHANGE, SET VOLUME
      {
         /* Go ahead and prepare for call to VSD */
         if (*pulMode & CDMC_LFT_CH && pInst->ulMode & CDMC_HEADPHONE)
            left = (USHORT)((ULONG)VOL_LEFT(*pulLevel) *
                   pInst->ulMasterVolume / 100);
         else           /* else channel is muted */
            left = 0;
         if (*pulMode & CDMC_RGT_CH && pInst->ulMode & CDMC_HEADPHONE)
            right = (USHORT)((ULONG)VOL_RIGHT(*pulLevel) *
                   pInst->ulMasterVolume / 100);
         else           /* else channel is muted */
            right = 0;
         pParam2->ulLevel = MAKEULONG(left, right);
         *pulParam1 |= MCI_SET_VOLUME;      //make sure VSD flag is set

      }  /* of if change */
      else
         rc = MCIERR_MISSING_FLAG;
   }  /* of if no error with flags */

   return(rc);

}  /* of SetAudio() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  SetConnector                                           */
/*                                                                          */
/* DESCRIPTIVE NAME:  Set Connector                                         */
/*                                                                          */
/* FUNCTION:  Enable or disable connectors from the MCI_CONNECTOR command.  */
/*            This function is called by ProcConnector().                   */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst    -- pointer to instance.                              */
/*      ULONG ulParam1 -- flag for this message.                            */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_DEVICE_NOT_READY  -- No Disc is present.                     */
/*      MCIERR_UNSUPPORTED_FLAG     -- cannot process constant.             */
/*      MCIERR_FLAGS_NOT_COMPATIBLE -- Invalid flag combinations.           */
/*      MCIERR_INVALID_FLAG         -- Unknown or unsupported flag.         */
/*      MCIERR_CANNOT_LOAD_DRIVER -- Unable to load VSD.                    */
/*                                                                          */
/* NOTES:   ProcConnector() calls this function only when                   */
/*          MCI_ENABLE_CONNECTOR or MCI_DISABLE_CONNECTOR flags were used.  */
/*                                                                          */
/****************************************************************************/

ULONG SetConnector(PINST pInst, ULONG ulParam1)
{
   ULONG rsp, rc = MCIERR_SUCCESS;          // assume the best
   MCI_STATUS_PARMS recStatus;

   /* get current position -- will be placed in pInst->ulCur_pos */
   /* don't use recStatus.ulReturn, it will be adjusted with     */
   /*   sync offset and current time format setting              */
   recStatus.ulItem = MCI_STATUS_POSITION;
   rsp = ProcStatus(pInst, MCI_STATUS_ITEM, &recStatus);

   if (ulParam1 & MCI_ENABLE_CONNECTOR)          // *** ENABLE DAC ***
   {
      if (pInst->ulMode & CDMC_CAN_DAC)
      {
         /* do it if not already playing internally */
         if (!(pInst->ulMode & CDMC_INTDAC))
            pInst->ulMode = pInst->ulMode & STREAM_MODE_SET | CDMC_INTDAC;

      }  /* of if can enable DAC */
      else
         rc = MCIERR_UNSUPPORTED_FLAG;           //no internal DAC
   }  /* of if enable */
   else                             // *** DISABLE DAC ***
   {
      if (pInst->ulMode & CDMC_INTDAC)
      {
         ProcStop(pInst, MCI_WAIT);
         pInst->ulMode ^= CDMC_INTDAC;
      }   /* of if playing internal DAC */
   }  /* of if disable */

   return(rc);

}  /* of SetConnector() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  SetCuePoint                                            */
/*                                                                          */
/* DESCRIPTIVE NAME:  Set Cue Point                                         */
/*                                                                          */
/* FUNCTION:  Set Cue Point from the MCI_SET_CUEPOINT command.              */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst    -- pointer to instance.                              */
/*      ULONG ulParam1 -- flag for this message.                            */
/*      PVOID pParam2  -- pointer to structure (message dependent).         */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_OUTOFRANGE -- invalid cuepoint position.                     */
/*      MCIERR_DEVICE_NOT_READY -- No Disc is present.                      */
/*      MCIERR_CUEPOINT_LIMIT_REACHED --  no more room to add events.       */
/*      MCIERR_DUPLICATE_CUEPOINT     --  duplicate cuepoint.               */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/****************************************************************************/

ULONG SetCuePoint(PINST pInst, MCI_CUEPOINT_PARMS *pParam2)
{
   ULONG rc, ulTime, ulFlags;
   int i, found = -1;
   MCI_CUEPOINT_PARMS recCuePoint;

   /* validate time */
   ulTime = GetTimeAddr(pInst, pParam2->ulCuepoint, TRUE);
   if (ulTime == (ULONG) -1L)
      rc = MCIERR_OUTOFRANGE;         // invalid track number
   else
   {
      ulTime += pInst->ulOffset;
      if (ulTime < pInst->ulStart_disk ||
          ulTime > pInst->ulEnd_disk)
         rc = MCIERR_OUTOFRANGE;
      else
         rc = MCIERR_SUCCESS;
   }

   /* make sure cuepoint is unique */
   if (!rc)
   {
      for (i=0; i < CDMCD_CUEPOINT_MAX; i++)
      {
         if (pInst->arrCuePoint[i] == (ULONG) -1L)
         {
            if (found < 0)
               found = i;      // save first available entry
         }
         else
            if (pInst->arrCuePoint[i] == ulTime)
            {
               rc = MCIERR_DUPLICATE_CUEPOINT;
               break;               // error end for loop
            }
      }  /* of for loop */

      if (!rc)
         if (found < 0)
            rc = MCIERR_CUEPOINT_LIMIT_REACHED;
   }  /* of if no error, test uniqueness */

   /* init record and relay message */
   if (!rc)
   {
      pInst->arrCuePoint[found] = ulTime;
      /* copy record so as not to change original from application */
      memcpy(&recCuePoint, pParam2, sizeof(MCI_CUEPOINT_PARMS));
      pParam2->ulCuepoint = ulTime;
      ulFlags = MCI_SET_CUEPOINT_ON | MCI_WAIT;
      rc = pInst->pMCIDriver(pInst->hHWMCID, MCI_SET_CUEPOINT,
                             &ulFlags, pParam2, 0);

      if (rc)                            // clear entry
         pInst->arrCuePoint[found] = -1L;

   }  /* of if no error, init record */

   return(rc);

}  /* of SetCuePoint() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  StatusMCD                                              */
/*                                                                          */
/* DESCRIPTIVE NAME:  Status MCD                                            */
/*                                                                          */
/* FUNCTION:  Query the Status of MCD flags.  This function is called       */
/*            by ProcStatus().                                              */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST  pInst    -- pointer to instance.                             */
/*      ULONG  ulParam1 -- flag for this message.                           */
/*      PVOID  pParam2  -- pointer to structure (message dependent).        */
/*      ULONG  rsp      -- state response.                                  */
/*      USHORT usIgnore -- ignore invalid media type.                       */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_OUTOFRANGE -- invalid track supplied.                        */
/*      MCIERR_FLAGS_NOT_COMPATIBLE -- Flags not compatible.                */
/*      MCIERR_UNSUPPORTED_FUNCTION -- Unknown flag or value used.          */
/*      MCIERR_DEVICE_NOT_READY   -- device was not ready, no disc.         */
/*      MCIERR_INVALID_MEDIA_TYPE -- No audio tracks were found.            */
/*                                                                          */
/* NOTES: ulParam1 = MCI_STATUS_ITEM or MCI_STATUS_ITEM | MCI_TRACK         */
/*                                   or MCI_STATUS_ITEM | MCI_STATUS_START  */
/*                                                                          */
/****************************************************************************/

ULONG StatusMCD(PINST pInst, ULONG ulParam1, MCI_STATUS_PARMS *pParam2,
                ULONG rsp, USHORT usIgnore)
{
   ULONG rc;
   ULONG ulAddr, ulTrack, ulFlags;
   MCI_CD_REGTRACK_REC *ptr1, *ptr2;
   MCI_STATUS_PARMS recStatus;
   ULONG ulMode;

   /* Make sure disc is present before you rely on cache */
   if (rsp)
   {
      if (rsp == MCIERR_INSTANCE_INACTIVE)
         if (pInst->usStatus - SUSPEND == REGTRACK)
            rc = MCIERR_INVALID_MEDIA_TYPE;
         else if (pInst->usStatus - SUSPEND <= NODISC)
            rc = MCIERR_DEVICE_NOT_READY;
         else
         {
            rc = MCIERR_SUCCESS;    // process inactive command
            recStatus.ulReturn = pInst->ulCur_pos;
         }
      else
         rc = rsp;
   }  /* of if known error */
   else
   {
      recStatus.ulItem = MCI_STATUS_POSITION;
      ulFlags = MCI_STATUS_ITEM | MCI_WAIT;
      rc = pInst->pMCIDriver(pInst->hHWMCID, MCI_STATUS,
                             &ulFlags, &recStatus, 0);
      if (ULONG_LOWD(rc))
      {
         rc = vsdResponse(pInst, rc);
         if (!rc)                    //Disc Change, get starting address
            recStatus.ulReturn = pInst->ulStart_disk;
      }  /* of if error */

      if (!ULONG_LOWD(rc))
         pInst->ulCur_pos = recStatus.ulReturn;    // valid value, keep it
   }  /* of else no error so check disc */

   if (rc == MCIERR_INVALID_MEDIA_TYPE && usIgnore)
      rc = MCIERR_SUCCESS;

   if (ULONG_LOWD(rc))
      return(rc);

   /*----------- End of checking presents of Disc -----------------*/

   switch (ulParam1)
   {
      case MCI_STATUS_ITEM | MCI_TRACK :
         rc = StatusMCDDef(pInst, ulParam1, pParam2, recStatus.ulReturn);
         break;
      case MCI_STATUS_ITEM | MCI_STATUS_START :
         /* item must be MCI_STATUS_POSITION from ProcStatus() */
         pParam2->ulReturn =  GetTimeAddr(pInst, pInst->ulStart_disk, FALSE);
         rc = GetTimeAddrRC(pInst, rc);
         break;
      default :                            // must be MCI_STATUS_ITEM alone
         switch(pParam2->ulItem)
         {
            case MCI_STATUS_POSITION :
               pParam2->ulReturn =  GetTimeAddr(pInst,
                         (ULONG)(recStatus.ulReturn - pInst->ulOffset), FALSE);
               rc = GetTimeAddrRC(pInst, rc);
               break;
            case MCI_STATUS_LENGTH :
               ulTrack = pInst->recTrack.ulBufSize /
                         sizeof(MCI_CD_REGTRACK_REC);
               ulAddr = (pInst->recTrack.TrackRecArr + ulTrack-1)->ulEndAddr -
                         pInst->recTrack.TrackRecArr->ulStartAddr;
               /* return MSF format for length if in TMSF format */
               if ((pInst->ulMode & TIME_MODE) == CDMC_TMSF)
               {
                  ulMode = pInst->ulMode;
                  pInst->ulMode = pInst->ulMode & TIME_MODE_SET | CDMC_REDBOOK;
                  pParam2->ulReturn = GetTimeAddr(pInst, ulAddr, FALSE);
                  rc = GetTimeAddrRC(pInst, rc);
                  pInst->ulMode = ulMode;
               }
               else
               {
                  pParam2->ulReturn = GetTimeAddr(pInst, ulAddr, FALSE);
                  rc = GetTimeAddrRC(pInst, rc);
               }
               break;
            case MCI_STATUS_NUMBER_OF_TRACKS :
               ptr1 = GetTrackInfo(pInst, 0, pInst->ulStart_disk);
               ptr2 = GetTrackInfo(pInst, 0, pInst->ulEnd_disk - 1);
               if (ptr1 && ptr2)         //if both pointers are valid
                  pParam2->ulReturn = ptr2->TrackNum - ptr1->TrackNum + 1;
               else
                  pParam2->ulReturn = 0L;
               ULONG_HIWD(rc) = MCI_INTEGER_RETURNED;
               break;
            default :
               rc = StatusMCDDef(pInst, ulParam1, pParam2, recStatus.ulReturn);

         }  /* of switch, known items */
   }  /* of switch, flags */

   return(rc);

}  /* of StatusMCD() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  StatusMCDDef                                           */
/*                                                                          */
/* DESCRIPTIVE NAME:  Status MCD Default                                    */
/*                                                                          */
/* FUNCTION:  Query the Status of MCD default flags.  This function is      */
/*            called by ProcStatus().                                       */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst    -- pointer to instance.                              */
/*      ULONG ulParam1 -- flag for this message.                            */
/*      PVOID pParam2  -- pointer to structure (message dependent).         */
/*      ULONG ulAddr   -- current address.                                  */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_OUTOFRANGE -- invalid track supplied.                        */
/*      MCIERR_FLAGS_NOT_COMPATIBLE -- Flags not compatible.                */
/*      MCIERR_UNSUPPORTED_FUNCTION -- Unknown flag or value used.          */
/*      MCIERR_DEVICE_NOT_READY   -- device was not ready, no disc.         */
/*      MCIERR_INVALID_MEDIA_TYPE -- No audio tracks were found.            */
/*                                                                          */
/* NOTES: ulParam1 = MCI_STATUS_ITEM or MCI_STATUS_ITEM | MCI_TRACK         */
/*                                   or MCI_STATUS_ITEM | MCI_STATUS_START  */
/*                                                                          */
/****************************************************************************/

ULONG StatusMCDDef(PINST pInst, ULONG ulParam1,
                   MCI_STATUS_PARMS *pParam2, ULONG ulAddr)
{
   ULONG rc = MCIERR_SUCCESS;
   ULONG ulTrack;
   ULONG ulMode;
   MCI_CD_REGTRACK_REC *ptr1;


   /****************** get track number and info ********************/

   if (ulParam1 & MCI_TRACK)
   {
      ulTrack = pParam2->ulValue;
      ptr1 = GetTrackInfo(pInst, (BYTE) ulTrack, 0);
      if (ptr1 == NULL)
         rc = MCIERR_OUTOFRANGE;
   }
   else   /* get current track */
   {
      ptr1 = GetTrackInfo(pInst, 0, ulAddr);   //valid addr from drive
      ulTrack = ptr1->TrackNum;
   }  /* of else get current track */

   if (!ULONG_LOWD(rc))
      switch(pParam2->ulItem)
      {
         case MCI_STATUS_NUMBER_OF_TRACKS : // with MCI_TRACK, w/o already done
            rc = MCIERR_FLAGS_NOT_COMPATIBLE;
         case MCI_STATUS_POSITION :     // with MCI_TRACK, w/o already done
            pParam2->ulReturn = GetTimeAddr(pInst,
                 (ULONG)(ptr1->ulStartAddr - pInst->ulOffset), FALSE);
            rc = GetTimeAddrRC(pInst, rc);
            break;
         case MCI_STATUS_POSITION_IN_TRACK :
            if (ulParam1 & MCI_TRACK)
               rc = MCIERR_FLAGS_NOT_COMPATIBLE;
            else
            {
               ulAddr -= ptr1->ulStartAddr + pInst->ulOffset;
               pParam2->ulReturn = GetTimeAddr(pInst, ulAddr, FALSE);
               rc = GetTimeAddrRC(pInst, rc);
            }
            break;
         case MCI_STATUS_LENGTH :       // with MCI_TRACK, w/o already done
            ulAddr = ptr1->ulEndAddr - ptr1->ulStartAddr;
            /* return MSF format for length if in TMSF format */
            if ((pInst->ulMode & TIME_MODE) == CDMC_TMSF)
            {
               ulMode = pInst->ulMode;
               pInst->ulMode = (pInst->ulMode & TIME_MODE_SET) | CDMC_REDBOOK;
               pParam2->ulReturn = GetTimeAddr(pInst, ulAddr, FALSE);
               rc = GetTimeAddrRC(pInst, rc);
               pInst->ulMode = ulMode;
            }
            else
            {
               pParam2->ulReturn = GetTimeAddr(pInst, ulAddr, FALSE);
               rc = GetTimeAddrRC(pInst, rc);
            }
            break;
         case MCI_STATUS_CURRENT_TRACK :
            if (ulParam1 & MCI_TRACK)
               rc = MCIERR_FLAGS_NOT_COMPATIBLE;
            else
            {
               pParam2->ulReturn = ulTrack;
               ULONG_HIWD(rc) = MCI_INTEGER_RETURNED;
            }
            break;
         case MCI_CD_STATUS_TRACK_TYPE :
            if (!(ptr1->TrackControl & IS_DATA_TRK))
               pParam2->ulReturn = MCI_CD_TRACK_AUDIO;
            else if (ptr1->TrackControl & IS_OTHER_TRK)
               pParam2->ulReturn = MCI_CD_TRACK_OTHER;
            else
               pParam2->ulReturn = MCI_CD_TRACK_DATA;
            ULONG_HIWD(rc) = MCI_TRACK_TYPE_RETURN;
            break;
         case MCI_CD_STATUS_TRACK_COPYPERMITTED :
            if (ptr1->TrackControl & IS_COPYABLE)
               pParam2->ulReturn = MCI_TRUE;
            else
               pParam2->ulReturn = MCI_FALSE;
            ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
            break;
         case MCI_CD_STATUS_TRACK_CHANNELS :
            if (!(ptr1->TrackControl & IS_DATA_TRK))      /* Is Audio ? */
               if (ptr1->TrackControl & HAS_4_CHANS)
                  pParam2->ulReturn = 4L;
               else
                  pParam2->ulReturn = 2L;
            else                                          /* Data */
               pParam2->ulReturn = 0L;
            ULONG_HIWD(rc) = MCI_INTEGER_RETURNED;
            break;
         case MCI_CD_STATUS_TRACK_PREEMPHASIS :
            if (!(ptr1->TrackControl & IS_DATA_TRK))      /* Is Audio ? */
               if (ptr1->TrackControl & IS_PRE_EMPH)
                  pParam2->ulReturn = MCI_TRUE;
               else
                  pParam2->ulReturn = MCI_FALSE;
            else                                          /* Data */
               pParam2->ulReturn = MCI_FALSE;
            ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
            break;
         /* no default value, all values were filtered in procStatus() */
      }  /* of switch, track info needed */

   return(rc);

}  /* of StatusMCDDef() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  StatusVSD                                              */
/*                                                                          */
/* DESCRIPTIVE NAME:  Status VSD                                            */
/*                                                                          */
/* FUNCTION:  Query the Status of VSD only flags.  This function is called  */
/*            by ProcStatus().                                              */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst    -- pointer to instance.                              */
/*      ULONG ulParam1 -- flag for this message.                            */
/*      PVOID pParam2  -- pointer to structure (message dependent).         */
/*      ULONG rc       -- initial state, inactive, not ready, etc.          */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_DEVICE_NOT_READY   -- device was not ready, no disc.         */
/*      MCIERR_INVALID_MEDIA_TYPE -- No audio tracks were found.            */
/*                                                                          */
/* NOTES: ulParam1 = MCI_STATUS_ITEM or MCI_STATUS_ITEM | MCI_TRACK         */
/*                                                                          */
/****************************************************************************/

ULONG StatusVSD(PINST pInst, ULONG ulParam1, MCI_STATUS_PARMS *pParam2,
                ULONG rc)
{
   ULONG ulVol;
   USHORT usStatus;
   ULONG ulFlags, rsp = 0L;
   MCI_STATUS_PARMS recStatus;

   if (!rc)
   {
      /* Query position to release any Disc Changes in VSD */
      recStatus.ulItem = MCI_STATUS_POSITION;
      ulFlags = MCI_STATUS_ITEM | MCI_WAIT;
      rc = pInst->pMCIDriver(pInst->hHWMCID, MCI_STATUS,
                             &ulFlags, &recStatus, 0);
      if (ULONG_LOWD(rc))
         rc = vsdResponse(pInst, rc);
   }  /* of if no error */

   if (rc == MCIERR_INVALID_MEDIA_TYPE)
   {
      rsp = MCIERR_INVALID_MEDIA_TYPE;
      rc = MCIERR_SUCCESS;
   }

   if (!rc)
   {
      ulParam1 |= MCI_WAIT;
      rc = pInst->pMCIDriver(pInst->hHWMCID, MCI_STATUS, &ulParam1,
                             pParam2, 0);
      if (ULONG_LOWD(rc))
         rc = vsdResponse(pInst, rc);       //if error, process response
   }  /* of if no error */

   if (!(ulParam1 & MCI_TRACK))          //process known combinations flags
      switch(pParam2->ulItem)
      {
         case MCI_STATUS_MEDIA_PRESENT :
            switch ULONG_LOWD(rc)
            {
               case MCIERR_SUCCESS :                //vsd already set ulReturn
                  ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
                  if (pParam2->ulReturn == MCI_FALSE)
                     pInst->usStatus = NODISC;
                  else if (rsp)
                     rc = rsp;                     //report invalid media
                  break;
               case MCIERR_INSTANCE_INACTIVE :
                  rc = MCIERR_SUCCESS;
                  ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
                  usStatus = (USHORT)(pInst->usStatus - SUSPEND);
                  if (usStatus == REGTRACK)
                     rc = MCIERR_INVALID_MEDIA_TYPE;
                  else if (usStatus <= NODISC)
                     pParam2->ulReturn = MCI_FALSE;
                  else
                     pParam2->ulReturn = MCI_TRUE;
                  break;
               default :                            //return not present
                  rc = MCIERR_SUCCESS;
                  ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
                  pParam2->ulReturn = MCI_FALSE;
            }  /* of switch */
            ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
            break;
         case MCI_STATUS_MODE :
            if (ULONG_LOWD(rc))
            {
               pParam2->ulReturn = MCI_MODE_NOT_READY;
               rc = MCIERR_SUCCESS;
            }
            else
               if (pParam2->ulReturn == MCI_MODE_STOP)
               {
                  if (pInst->usStatus == PAUSED)
                     pParam2->ulReturn = MCI_MODE_PAUSE;
               }
               else
                  if (pParam2->ulReturn == MCI_MODE_NOT_READY)
                     pInst->usStatus = NODISC;

            ULONG_HIWD(rc) = MCI_MODE_RETURN;
            break;
         case MCI_STATUS_READY :
            if (ULONG_LOWD(rc))
            {
               pParam2->ulReturn = MCI_FALSE;
               rc = MCIERR_SUCCESS;
            }
            else
               if (pParam2->ulReturn == MCI_FALSE)
                  pInst->usStatus = NODISC;

            ULONG_HIWD(rc) = MCI_TRUE_FALSE_RETURN;
            break;
         case MCI_STATUS_VOLUME :
            if (ULONG_LOWD(rc))          // if error
               rsp = TRUE;
            else
            {
               rsp = FALSE;
               ULONG_HIWD(rc) = MCI_COLONIZED2_RETURN;

               /* adjust device volume to component volume */
               if (pInst->ulMasterVolume && pInst->ulMode & CDMC_HEADPHONE)
               {
                  ulVol = (ULONG)VOL_LEFT(pParam2->ulReturn) *
                        100 / pInst->ulMasterVolume;
                  if (ulVol > 100)
                     VOL_LEFT(pParam2->ulReturn) = 100;
                  else
                     VOL_LEFT(pParam2->ulReturn) = (BYTE)ulVol;

                  ulVol = (ULONG)VOL_RIGHT(pParam2->ulReturn) *
                           100 / pInst->ulMasterVolume;
                  if (ulVol > 100)
                     VOL_RIGHT(pParam2->ulReturn) = 100;
                  else
                     VOL_RIGHT(pParam2->ulReturn) = (BYTE)ulVol;

               }  /* of if non-zero */
               else
                  /* master volume is 0%, return requested volume */
                  rsp = TRUE;

            }  /* of else no error */

            if (rsp)  //unknown volume, ask VSD what component volume should be
            {
               if (rc == MCIERR_INSTANCE_INACTIVE)
                  pParam2->ulReturn = pInst->recSave.ulLevel;
               else
               {
                  ulFlags = MCI_WAIT;
                  recStatus.ulReturn = pInst->ulLevel;
                  pInst->pMCIDriver(pInst->hHWMCID, MCIDRV_CD_STATUS_CVOL,
                                    &ulFlags, &recStatus, 0);
                  pParam2->ulReturn = recStatus.ulReturn;
               }
               rc = MAKEULONG(MCIERR_SUCCESS, MCI_COLONIZED2_RETURN);

            }  /* of if unknown volume */

            break;
      }  /* of switch */

   return(rc);

}  /* of StatusVSD() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  DisableEvents             DEVELOPER:  Garry Lewis      */
/*                                                                          */
/* DESCRIPTIVE NAME:  Disable Events                                        */
/*                                                                          */
/* FUNCTION:  Disable cuepoint and position advise events from the VSD.     */
/*            This is a requirement when the disc is changed.               */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst    -- pointer to instance.                              */
/*                                                                          */
/* EXIT CODES:  None                                                        */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/****************************************************************************/

VOID DisableEvents(PINST pInst)
{
   int i;
   ULONG ulParam1;
   MCI_CUEPOINT_PARMS recCuePoint;

   /* disable cuepoints */
   ulParam1 = MCI_SET_CUEPOINT_OFF | MCI_WAIT;
   for (i=0; i < CDMCD_CUEPOINT_MAX; i++)
   {
      if (pInst->arrCuePoint[i] != -1L)
      {
         recCuePoint.ulCuepoint = pInst->arrCuePoint[i];
         pInst->pMCIDriver(pInst->hHWMCID, MCI_SET_CUEPOINT,
                           &ulParam1, &recCuePoint, 0);
         pInst->arrCuePoint[i] = (ULONG) -1L;
      }  /* of if cuepoint is set */
   }  /* of for loop of cuepoints */

   /* disable position advise */
   ulParam1 = MCI_SET_POSITION_ADVISE_OFF | MCI_WAIT;
   pInst->pMCIDriver(pInst->hHWMCID, MCI_SET_POSITION_ADVISE,
                     &ulParam1, 0L, 0);

}  /* of DisableEvents() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  GetTimeAddr                                            */
/*                                                                          */
/* DESCRIPTIVE NAME:  Get Time Address                                      */
/*                                                                          */
/* FUNCTION:  Convert to and from MMTime and other time formats.            */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST  pInst   -- pointer to instance.                              */
/*      ULONG  ulAddr  -- input address.                                    */
/*      USHORT usTO    -- TRUE if to MMTime, FALSE if from MMTime.          */
/*                                                                          */
/* EXIT CODES:  ULONG  ulReturn -- the converted address.                   */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

ULONG GetTimeAddr(PINST pInst, ULONG ulAddr, USHORT usTO)
{
   ULONG ulReturn;
   MCI_CD_REGTRACK_REC *ptr;

   switch (pInst->ulMode & TIME_MODE)
   {
      case CDMC_MILLSEC :                          // milliseconds
         if (usTO)
            ulReturn = MSECTOMM(ulAddr);
         else
            ulReturn = MSECFROMMM(ulAddr);
         break;
      case CDMC_REDBOOK :                          // Red Book / MSF
         if (usTO)
            ulReturn = REDBOOKTOMM(ulAddr);
         else
            ulReturn = REDBOOKFROMMM(ulAddr);
         break;
      case CDMC_TMSF :                             // Track:Min:Sec:Frame
         if (usTO)
         {
            ptr = GetTrackInfo(pInst, TMSF_TRACK(ulAddr), 0L);
            if (ptr == NULL)
               ulReturn = (ULONG) -1L;            // invalid track number
            else
            {
               ulAddr = (ulAddr >> 8) & 0x00FFFFFF;     //TMSF -> MSF
               ulReturn = ptr->ulStartAddr + REDBOOKTOMM(ulAddr);
            }
         }  /* of TO MMTIME */
         else
         {
            ptr = GetTrackInfo(pInst, 0, ulAddr);
            if (ptr == NULL)
               ulReturn = (ULONG) -1L;            // invalid track number
            else
            {
               ulAddr -= ptr->ulStartAddr;
               ulReturn = (REDBOOKFROMMM(ulAddr) << 8) | (ULONG) ptr->TrackNum;
            }
         }  /* of else FROM MMTIME */
         break;
      case CDMC_MMTIME :                          // Multimedia Time
      default :
         ulReturn = ulAddr;
   }  /* on switch */

   return(ulReturn);

}  /* of GetTimeAddr() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  GetTimeAddrRC                                          */
/*                                                                          */
/* DESCRIPTIVE NAME:  Get Time Address Return Code                          */
/*                                                                          */
/* FUNCTION:  Return correct time format in the colinized code,             */
/*            high word of the return code.                                 */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST  pInst   -- pointer to instance.                              */
/*      ULONG  ulAddr  -- input address.                                    */
/*      USHORT usTO    -- TRUE if to MMTime, FALSE if from MMTime.          */
/*                                                                          */
/* EXIT CODES:  ULONG  rc  -- the converted return code.                    */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

ULONG GetTimeAddrRC(PINST pInst, ULONG rc)
{
   switch (pInst->ulMode & TIME_MODE)
   {
      case CDMC_REDBOOK :                          // Red Book / MSF
         ULONG_HIWD(rc) = MCI_COLONIZED3_RETURN;
         break;
      case CDMC_TMSF :                             // Track:Min:Sec:Frame
         ULONG_HIWD(rc) = MCI_COLONIZED4_RETURN;
         break;
      case CDMC_MILLSEC :                          // milliseconds
      case CDMC_MMTIME :                           // Multimedia Time
      default :
         ULONG_HIWD(rc) = MCI_INTEGER_RETURNED;
   }  /* on switch */

   return(rc);

}  /* of GetTimeAddrRC() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  GetTrackInfo                                           */
/*                                                                          */
/* DESCRIPTIVE NAME:  Get Track Information                                 */
/*                                                                          */
/* FUNCTION:  Find the Track entry for the given track or absolute address. */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst    -- pointer to instance.                              */
/*      BYTE  Track    -- specified track number.                           */
/*      ULONG ulAddr   -- specified absolute address, ignored if ulTrack    */
/*                        is non-zero.                                      */
/*                                                                          */
/* EXIT CODES:  MCI_CD_REGTRACK_REC *pRet -- return pointer of track info,  */
/*                                           NULL if error (invalid input). */
/* NOTES:                                                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

MCI_CD_REGTRACK_REC * GetTrackInfo(PINST pInst, BYTE Track, ULONG ulAddr)
{
   MCI_CD_REGTRACK_REC *pRet;
   USHORT i, usOffset;                  //Number of Tracks - 1

   if (Track)                                         // if a track was used
      if (pInst->recDisc.LowestTrackNum <= Track &&      // and is valid
          pInst->recDisc.HighestTrackNum >= Track)
         pRet = pInst->recTrack.TrackRecArr +            // get track info
                  (Track - pInst->recDisc.LowestTrackNum);
      else
         pRet = NULL;                                    // else error
   else                                                // otherwise an address
   {
      usOffset = pInst->recDisc.HighestTrackNum - pInst->recDisc.LowestTrackNum;

      /* check if address is in a valid range */
      if (pInst->recTrack.TrackRecArr->ulStartAddr <= ulAddr &&
          (pInst->recTrack.TrackRecArr + usOffset)->ulEndAddr >= ulAddr)
      {
         /* for every track, check address bounds until ulAddr is found */
         /* if not found it must be the last track since the addr is valid */
         for (i= 0; i < usOffset; i++)
            if ((pInst->recTrack.TrackRecArr + i)->ulStartAddr <= ulAddr &&
                (pInst->recTrack.TrackRecArr + i)->ulEndAddr > ulAddr)
               break;
         pRet = pInst->recTrack.TrackRecArr + i;

      }  /* of if a valid address */
      else
         pRet = NULL;                                    // else error
   }  /* of else an address is used */

   return(pRet);

}  /* of GetTrackInfo() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  ValAddress                                             */
/*                                                                          */
/* DESCRIPTIVE NAME:  Validate Address                                      */
/*                                                                          */
/* FUNCTION:  Validate Redbook Audio address in MMTIME format.              */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST  pInst    -- pointer to instance.                             */
/*      ULONG  *pusFrom -- address to be tested and returned.               */
/*      ULONG  *pusTo   -- address to be tested and returned.               */
/*      USHORT isPlay   -- type of check (TRUE = play, FALSE - seek).       */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_OUTOFRANGE -- cannot play in reverse.                        */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

ULONG ValAddress(PINST pInst, ULONG *pulFrom, ULONG *pulTo, USHORT isPlay)
{
   ULONG rc;

   rc = MCIERR_SUCCESS;

   /* validate TO */
   if (*pulTo < pInst->ulStart_disk)
      rc = MCIERR_OUTOFRANGE;
   else
      if (isPlay)
      {
         if (*pulTo > pInst->ulEnd_disk)
            rc = MCIERR_OUTOFRANGE;
      }
      else
         /* ulEnd_disk is really the starting frame or sector of the next */
         /* track, usually the Lead Out Track.  Valid seeks should be     */
         /* contained within playable tracks.                             */
         if (*pulTo > pInst->ulEnd_disk - MMT_FRAME)
            rc = MCIERR_OUTOFRANGE;

   /* Validate from address if needed */
   if (!rc && isPlay)
   {
      if (*pulFrom < pInst->ulStart_disk)
         rc = MCIERR_OUTOFRANGE;
      else if (*pulTo > pInst->ulEnd_disk)
         rc = MCIERR_OUTOFRANGE;

      /* validate direction */
      /* can only go forward if HW can't play in reverse or if streaming */
      if (*pulFrom > *pulTo &&
             (!(pInst->usCaps & CDVSD_CAP_CAN_REVERSE) ||
               (pInst->ulMode & CDMC_STREAM)))
         rc = MCIERR_OUTOFRANGE;

   }  /* of if validating FROM */

   return(rc);

}  /* of ValAddress() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  ValState                                               */
/*                                                                          */
/* DESCRIPTIVE NAME:  Validate State                                        */
/*                                                                          */
/* FUNCTION:  Validate State of an Instance.  Most commands need to have    */
/*            a disc and not be suspended.                                  */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst     -- pointer to instance.                             */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_SUCCESS    -- action completed without error.                */
/*      MCIERR_DEVICE_NOT_READY -- No Disc is present.                      */
/*      MCIERR_INSTANCE_INACTIVE -- Instance is suspended.                  */
/*      MCIERR_INVALID_MEDIA_TYPE -- No audio tracks were found.            */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

ULONG ValState(PINST pInst)
{
   ULONG rc;

   /* if tracks were registered then we should be stopped, */
   /*    unless no audio tracks exists.                    */
   if (pInst->usStatus == REGTRACK)
      rc = MCIERR_INVALID_MEDIA_TYPE;
   else if (pInst->usStatus <= NODISC)
      rc = MCIERR_DEVICE_NOT_READY;
   else if (pInst->usStatus >= SUSPEND)
      rc = MCIERR_INSTANCE_INACTIVE;
   else
      rc = MCIERR_SUCCESS;

   return(rc);

}  /* of ValState() */


/****************************************************************************/
/*                                                                          */
/* SUBROUTINE NAME:  vsdResponse                                            */
/*                                                                          */
/* DESCRIPTIVE NAME:  Vendor Supplied Driver Response                       */
/*                                                                          */
/* FUNCTION:  Process the response from a VSD.                              */
/*                                                                          */
/* PARAMETERS:                                                              */
/*      PINST pInst      -- pointer to instance.                            */
/*      ULONG ulResponse -- received response.                              */
/*                                                                          */
/* EXIT CODES:                                                              */
/*      MCIERR_DEVICE_NOT_READY   -- device was not ready, no disc.         */
/*      MCIERR_INVALID_MEDIA_TYPE -- No audio tracks were found.            */
/*                                                                          */
/* NOTES:                                                                   */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

ULONG vsdResponse(PINST pInst, ULONG ulResponse)
{
   ULONG rc;

   switch (ULONG_LOWD(ulResponse))
   {
      case MCIERR_DEVICE_NOT_READY :
         pInst->usStatus = NODISC;
         rc = ulResponse;
         break;
      case MCIERR_MEDIA_CHANGED :
         rc = ReRegister(pInst);
         break;
      default :
         rc = ulResponse;
   }   /* of switch */

   return(rc);

}  /* of vsdResponse() */


