[Back to SOUND SWAG index] [Back to Main SWAG index] [Original]
{
TK> Monday March 13 1995 21:16, Ville Lumme wrote to All:
SC>
> Has anyone got some kind of specs how to interrupt MSCDEX to play audio
> CDs? I have spent much time for searching them, and I'm getting hopeless...
TK> Read my answer from Netmail.
SC> Can you do a post in echomail. Others might want to read it
SC> too.
Okey sounds like you want a unit (or a routine) for playing audio cds. Well, I
have one, here the source:
}
{$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 volumes to }
Move(CDControl[1],InfRec.VolInf,8){ 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}
[Back to SOUND SWAG index] [Back to Main SWAG index] [Original]