{Newsgroups: comp.lang.pascal
Path: news.kub.nl!news.nic.surfnet.nl!sun4nl!EU.net!howland.reston.ans.net!torn!info!lang1250
From: lang1250@mach1.wlu.ca (Todd Lang u)
Subject: Re: PLAYING CD-ROM'S
X-Newsreader: TIN [version 1.1 PL6]
References: <USERID-140295090341@msp81-10.nas.mr.net>
Message-ID: <D4034J.66w@info.uucp>
Nntp-Posting-Host: mach1.wlu.ca
Sender: news@info.uucp (news management)
Organization: Wilfrid Laurier University
Date: Tue, 14 Feb 1995 17:26:43 GMT
Lines: 381

 (USERID@InforMNs.k12.MN.US) wrote:
: Hi, does someone have a rutine to play sound cd's with a cd-rom.
: I have been locking for a routine like that for a while but no one can
: help me..
:
As you can see, this is Greg Estabrooks unit (sort of).  Give credit to
him, though I don't have a clue what his e-mail address is, or how I came
to possess his unit.  However, I have included one routine in there to
make it slightly easier.  His unit used to not be able to get the CD
device name and you had to enter it manually, or hard-code it.  This has
been change and it will now automagically find the device name.

---------------If you cut here, you ruined your monitor!-------------------

{$X+,O+,F+}
UNIT CDRom;              { CD Rom Interfacing routines.                 }
                         { Last Updated July 16/94, Greg Estabrooks.    }
INTERFACE
{***********************************************************************}
USES DOS;
CONST
   CDRead  = $4402;
   CDWrite = $4403;

             { Define some CD IOCTL OutPut function codes.              }
   EjectDisk   = 0;
   LockUnlock  = 1;
   ResetCD     = 2;
   AudioCtrl   = 3;
   WriteCtrlStr= 4;
   CloseTray   = 5;

TYPE
CDPosType = RECORD
   CtlAdr : BYTE;  { Control and ADR byte.                 }
   Track  : BYTE;  { Current Track #.                      }
   Indx   : BYTE;  { Point or Index Byte.                  }
   Min    : BYTE;  { Minute. \                             }
   Sec    : BYTE;  { Second.  > Running time within track. }
   Frame  : BYTE;  { Frame.  /                             }
   Zero   : BYTE;  { Should be a 0.                        }
   DMin   : BYTE;  { Minute. \                             }
   DSec   : BYTE;  { Second.  > Running time on disk.      }
   DFrame : BYTE;  { Frame.  /                             }
END;

CDInfoRecord = RECORD
   Status : WORD; { Holds the status of last operation. }
   NumCD  : WORD; { Number of CD Drives available.      }
   DrvChar: CHAR; { First CD drive in CHAR format.      }
   DrvNo  : BYTE; { BYTE value of first drive. 0 = A,ETC}
   DVParam: LONGINT;{ Device parameters.                }
   VolInf : ARRAY[1..8] OF BYTE; { Holds Audio Channel inf.}
   LoTrack: BYTE; { Lowest Audio track #.               }
   HiTrack: BYTE; { Highest Audio Track #.              }
   LdAdr  : LONGINT;{ Address of Lead track in HSG.     }
END;

DriveList = RECORD
   UnitCode : BYTE;
   DOffset,DSegment : WORD;
END;

VAR
   CDDevice :TEXT;              { File Handle for the CD Driver.        }
   CDStatus :WORD;              { Status for last CD operation.         }
   CDControl:ARRAY[0..200] OF BYTE;
   CDHandle :WORD;
   CDInf    :CDInfoRecord;
   PosInf   :CDPosType;

FUNCTION CDGetHandle : WORD;
                         { Routine to get handle for referencing the CD }
                         { Device driver.                               }
PROCEDURE CDCloseHandle;
                         { Routine to close Handle referencing the CD   }
                         { Driver.                                      }
FUNCTION CDIoctl(IntFunc, Len : WORD; VAR CtlBlk) : BOOLEAN;
                         { Routine to call the CD IOCTL.                }
PROCEDURE DriverRequest(Drive : BYTE; VAR CtlBlk);
                         { Routine to make request of MSCDEX.           }
FUNCTION CDEject : BOOLEAN;
                         { Routine to Eject the CD tray.                }
FUNCTION CDCloseTray : BOOLEAN;
                         { Routine to close the CD tray.                }
FUNCTION CDReset : BOOLEAN;
                         { Routine to reset the CD Drive.               }
FUNCTION CDGetVol(VAR InfRec : CDInfoRecord) : BOOLEAN;
                         { Routine to get current audio volume output.  }
FUNCTION CDStop : BOOLEAN;
                         { Routine to stop the playing and Audio CD.    }
PROCEDURE CDInitInfo;
                         { Routine to Intilialize CD Info.              }
FUNCTION CDResumePlay : BOOLEAN;
                         { Routine to Resume playing a previously stopped}
                         { audio track.                                  }
FUNCTION CDGetPos(VAR PosInf : CDPosType ) : BOOLEAN;
                         { Routine to retrieve current position being   }
                         { played.                                      }
FUNCTION Red2HSG(Inf : LONGINT ) : LONGINT;

FUNCTION CDGetTrackStart(Track : BYTE ) : LONGINT;

FUNCTION CDVolSize : LONGINT;
                         { Routine to determine the volume size in      }
                         { sectors.                                     }
FUNCTION CDSectSize : WORD;
                         { Routine to determine the Sector size in      }
                         { bytes.                                       }
FUNCTION CDPlayAudio(Track : BYTE; Len : LONGINT ) : BOOLEAN;

IMPLEMENTATION
{***********************************************************************}
VAR
   OldExit : POINTER;
   CDDL     : DriveList;
   CDDriver : STRING[8];  { CD Driver name.                }

FUNCTION GetDriverName : String;
VAR
   CDNTemp : Array[1..18] OF BYTE;
   Where : POINTER;
   Count : BYTE;
   CDSTemp : STRING[8];
BEGIN
   ASM
      MOV AX, 1501h
      MOV BX, OFFSET CDDL
      MOV DX, SEG CDDL
      MOV ES, DX
      INT $2F
   END;
   Where := Ptr(CDDL.DSegment,CDDL.DOffset);
   Move(Where^,CDNTEMP,18);
   Count := 1;
   REPEAT
      CDStemp[Count] := CHR(CDNTemp[10+Count]);
      INC(Count);
   UNTIL (Count > 8) OR (CDNTemp[10+Count]=32);
   CDSTemp[0] := CHR(Count-1);
   GetDriverName := CDSTemp;
END;


FUNCTION CDGetHandle : WORD;
{ Routine to get handle for referencing the CD }
{ Device driver.                               }
VAR
   Handle : WORD;
BEGIN
   Assign(CDDevice,CDDriver);     { Assign Handle to driver.              }
   {$I-}                          { Turn I/O checking off.                }
   Reset(CDDevice);               { Attempt to open driver.               }
   {$I+}                          { Turn I/O checking on.                 }
   IF (IOResult = 0) THEN
      Handle := TextRec(CDDevice).Handle{ Save DOS Handle.                }
    ELSE
      Handle := 0;
   CDGetHandle := Handle;
END;{CDGetHandle}

PROCEDURE CDCloseHandle;
{ Routine to close Handle referencing the CD   }
{ Driver.                                      }
BEGIN
   {$I-}                          { Turn I/O checking off.                }
   Close(CDDevice);               { Attempt to Close driver.              }
   {$I+}                          { Turn I/O checking on.                 }
   IF (IOResult = 0) THEN           { Dummy IF to clear IOResult.           }
   BEGIN
   END;
END;{CDCloseHandle}

FUNCTION CDIoctl(IntFunc, Len : WORD;VAR CtlBlk ) : BOOLEAN; ASSEMBLER;
{ Routine to call the CD IOCTL.                }
ASM
  PUSH   DS
  MOV    AX,IntFunc                { 4402 = Read, 4403 = Write.            }
  MOV    BX,CDHandle               { Load Handle for Driver into BX.       }
  MOV    CX,Len
  LDS    DX,CtlBlk                 { Point DS:DX to the control block.     }

  INT    $21                       { Call DOS Interrupt.                   }
  MOV    CDStatus,AX               { Save status of function.              }
  JNC    @NoError                  { If there was no error jump to noerror.}
  MOV    AX,0                      { Return FALSE.                         }
@NoError:
  MOV    AX,1                      { Return TRUE.                          }
@Exit:
  POP    DS
END;{CDIoctl}

PROCEDURE DriverRequest(Drive : BYTE;VAR CtlBlk ); ASSEMBLER;
{ Routine to make request of MSCDEX.           }
ASM
   PUSH  ES                      { Save ES.                              }
   MOV   AX,$1510                { Subfunction to make request of CD driver.}
   XOR   CH,CH                   { Clear High byte of CX.                }
   MOV   CL,Drive                { Load drive to make request of. 0 = A,etc}
   LES   BX,CtlBlk               { Point ES:BX to the control block.     }
   INT $2F                       { Call Multiplex interrupt.             }
   POP ES                        { Restore ES.                           }
END;{DriverRequest}

FUNCTION CDEject : BOOLEAN;                 { Routine to Eject the CD tray}
BEGIN
   CDControl[0] := EjectDisk;               { Function code to eject CD.  }
   CDEject := CDIoctl(CDWrite,1,CDControl); { Now try function and return }
                                            { result to program/user.     }
END;{CDEject}

FUNCTION CDCloseTray : BOOLEAN;             { Routine to close the CD tray}
BEGIN
   CDControl[0] := CloseTray;               { Function code to close CD.  }
   CDCloseTray := CDIoctl(CDWrite,1,CDControl);{ Now try function and return}
                                            { result to program/user.     }
END;{CDCloseTray}

FUNCTION CDReset : BOOLEAN;                 { Routine to reset the CD Drive}
BEGIN
   CDControl[0] := ResetCD;                 { Function code to Reset drive.}
   CDReset := CDIoctl(CDWrite,1,CDControl); { Now try function and return  }
                                            { result to program/user.      }
END;{CDReset}

FUNCTION CDGetVol(VAR InfRec : CDInfoRecord) : BOOLEAN;
{ Routine to get current audio volume output.  }
VAR
   Temp : BOOLEAN;                          { Holds IOCTL Read Result.     }
BEGIN
   CDControl[0] := 4;                     {Function to read current volumes}
   Temp := CDIoctl(CDRead,8,CDControl);
   IF Temp THEN                             { IF all was fine then save    }
      Move(CDControl[1],InfRec.VolInf,8)    { volumes to array.            }
    ELSE
      FillChar(InfRec.VolInf,8,#0);         { Otherwise zero the array.    }
   CDGetVol := Temp;                        { Return proper result.        }
END;{CDGetVol}

FUNCTION CDStop : BOOLEAN;      { Routine to stop the playing and Audio CD.}
BEGIN
   FillChar(CDControl,Sizeof(CDControl),#0);
   CDControl[0] := 5;                       { Byte length of request header}
   CDControl[1] := 0;                       { Sub unit #.                  }
   CDControl[2] := $85;                     { Function to stop CD.         }
   DriverRequest(CDInf.DrvNo,CDControl);
   CDStatus := CDControl[3] OR CDControl[4] SHL 8;
   CDStop   := (CDStatus AND $8000) = 0;
END;{CDStop}

PROCEDURE CDInitInfo;
{ Routine to Intilialize CD Info.              }
BEGIN
   ASM
     MOV  AX,$1500                  { Function to get installation info.    }
     MOV  BX,0                      { Clear BX.                             }
     INT  $2F                       { Call CD Multiplex.                    }
     MOV  CDInf.NumCD,BX            { Save number of CD drives available.   }
     MOV  CDInf.DrvNo,CL            { Save first drive number.              }
     MOV  CDInf.DrvChar,CL          { Save Drive number and convert it to   }
     ADD  CDInf.DrvChar,'A'         { a CHAR. I.E A,B,C,ETC                 }
   END;

  FillChar(CDControl,SizeOf(CDControl),#0);
  CDControl[0] := $0A;           { Function to get Audio Disk info.      }
  CDIoctl(CDRead,6,CDControl);
  Move(CDControl[1],CDInf.LoTrack,6);
END;{CDInitInfo}

FUNCTION CDResumePlay : BOOLEAN;
{ Routine to Resume playing a previously stopped}
{ audio track.                                  }
BEGIN
   FillChar(CDControl,Sizeof(CDControl),#0);
   CDControl[0] := 5;             { Byte length of request header.        }
   CDControl[1] := 0;             { Sub unit #.                           }
   CDControl[2] := $88;           { Function to Resume play a CD.         }
   DriverRequest(CDInf.DrvNo,CDControl);
   CDStatus := CDControl[3] OR CDControl[4] SHL 8;
   CDResumePlay := (CDStatus AND $8000) = 0;
END;{CDResumePlay}

FUNCTION CDGetPos(VAR PosInf : CDPosType ) : BOOLEAN;
{ Routine to retrieve current position being   }
{ played.                                      }
BEGIN
   CDControl[0] := $0C;           { Function to get Audio Postion info.   }
   CDGetPos := CDIoctl(CDRead,10,CDControl);
   Move(CDControl[1],PosInf,10);
END;{CDGetPos}

FUNCTION Red2HSG(Inf : LONGINT ) : LONGINT;
VAR
   Temp :LONGINT;
BEGIN
   Temp :=        LONGINT(( Inf SHR 16 ) AND $FF )  * 4500;
   Temp := Temp + LONGINT(( Inf SHR  8 ) AND $FF )  * 75;
   Temp := Temp + LONGINT(( Inf ) AND $FF ) ;
   Red2HSG := Temp - 2;
END;{Red2HSG}

FUNCTION CDGetTrackStart(Track : BYTE ) : LONGINT;
VAR
   TrackInf :ARRAY[0..6] OF BYTE;
   Start    :LONGINT;
BEGIN
   TrackInf[0] := $0B;            { Function to get track info.           }
   TrackInf[1] := Track;          { Track to get information of.          }

   CDIoctl(CDRead,6,TrackInf);
   Move(TrackInf[2],Start,4);
   CDGetTrackStart := Red2HSG(Start);
END;{CDGetTrackStart}

FUNCTION CDVolSize : LONGINT;
{ Routine to determine the volume size in      }
{ sectors.                                     }
VAR
   TempLong : LONGINT;           { Holds temporary size info.            }
BEGIN
   CDControl[0] := 8;            { Function code to determine volume size.}
   CDIoctl(CDRead,4,CDControl);  { Now get information.                   }
   Move(CDControl[1],TempLong,4);
   CDVolSize := TempLong;
END;{CDVolSize}

FUNCTION CDSectSize : WORD;
{ Routine to determine the Sector size in      }
{ bytes.                                       }
VAR
   TempWord :WORD;              { Holds temporary size info.            }
BEGIN
   CDControl[0] := 7;            { Function code to determine Sector size.}
   CDIoctl(CDRead,4,CDControl);  { Now get information.                   }
   Move(CDControl[2],TempWord,2);
   CDSectSize := TempWord;
END;{CDSectSize}

FUNCTION CDPlayAudio(Track : BYTE;Len : LONGINT ) : BOOLEAN;
VAR
   TrackStart : LONGINT;
BEGIN
   FillChar(CDControl,SizeOf(CDControl),#0); { Clear Control block.       }
   CDControl[0] := 22;            { Length of request header.             }
   CDControl[1] := 0;             { Zero the sub unit.                    }
   CDControl[2] := $84;           { Function to play audio.               }
   TrackStart := CDGetTrackStart(Track);
   Move(TrackStart,CDControl[14],4);
   Move(Len,CDControl[18],4);     { # of sectors to play.                 }
   DriverRequest(CDInf.DrvNo,CDControl);
   CDStatus := CDControl[3] OR CDControl[4] SHL 8;
   CDPlayAudio := (CDStatus AND $8000) = 0;
END;{CDPlayAudio}

{$F+}
PROCEDURE CDExit;
{ Our Exiting routine to clean up after our    }
{ selves.                                      }
BEGIN
   ExitProc := OldExit;           { Restore original exit procedure.      }
   CDCloseHandle;                 { Close the handle for the CD Driver.   }
END;
{$F-}

BEGIN

   CDDriver := GetDriverName;
   CDHandle := CDGetHandle;
   IF (CDHandle <> 0) THEN
   BEGIN
      CDInitInfo;
      OldExit := ExitProc;
      ExitProc := @CDExit;
   END;
END.{CDRom}
{*************************************************************************}

