unit cdrom; { this program will allow the control of multiple cd-rom audio drives simultaniously. I am not sure just yet what sort of an interface I'm going to provide but we will soon see eh? } { prototype #1, just get talking to an audio CD-ROM } interface uses crt; const MAX_TRACKS_PER_CD = 30; type TAudioTrackRec = record code : byte; track : byte; startPoint : longint; trackInfo : byte; end; TAudioDiskRec = record code : byte; lowTrack : byte; highTrack : byte; startPoint : longint; end; TTrackRec = record number : byte; start : longint; finish : longint; end; TTrackArray = array[1 .. MAX_TRACKS_PER_CD] of TTrackRec; var tracks : TTrackArray; { This is a VERY simple CD-player. It only plays from the first { available CD, and only tracks from 1 - 9 and stops after every { track... but it will give you and idea! } procedure playATrack; {Stop the CD player "drive", no errors, even when already stopped} procedure stopCD( drive : word ); { Play a track from the presumably already loaded tracks array { NO ERROR CHECKING ON THIS!. Programmer to make sure that { this procedured is proceeded at some point by calling of the { procedure "getAllTrackDetails"} procedure playTrack( drive, track : word ); { Play from sectors start to finish on drive. { NO ERROR CHECKING } procedure playTrack2(drive : word; start, finish : longint); { If used after a STOPCD, this will resume play at where the CD { was stopped, assuming same drive } procedure resumePlay( drive : word ); { Gets all the track details for the currently loaded CD and places { it into the "TRACKS" array } procedure getAllTrackDetails( drive : word ); { Stops and causes the CD tray to eject (if supported) } procedure ejectCD( drive : word ); { Closes the CD tray (not supported under OS/2, and many CD players } procedure closeCD( drive : word ); { Locks the CD tray, prevents ejection from eject button pressing } procedure lockCD( drive : word ); { Unlocks the CD track, allows ejection from eject button pressing } procedure unlockCD( drive : word ); { Refreshes the buffers in the CD player, ensures the information is { correct } procedure resetCD( drive : word ); { Get the Universal Product code of the CD, not usually available } procedure getUPC( drive : word; var upc : string ); { Get the VTOC from the CD } procedure readVTOC( drive : word ); { Get the details of a particular track on the CD } procedure getTrackDetails( drive : word; trk : byte; var t : TAudioTrackRec ); { Get the number of CD players, and the first CD number } function getNumberOfCDs( var sLetter : word ) : word; function getUnique( drive : word ) : string; { Get the Details of the currently loaded CD } function getCDDetails( drive : word; var t : TAudioDiskRec ) : word; { Get the number of tracks of the currently loaded CD } function getNumberOfTracks( drive : word ) : word; { Get the running time of the currently playing track } function getRunningTime( drive : word; var min,sec : byte ) : word; { Returns true if the CD player has been changed and no update of { information has occured } function mediaChanged( drive : word ) : boolean; { Returns TRUE if the CD-tray is ejected } function doorOpen(drive : word) : boolean; implementation const EJECT_TRAY = 0; CLOSE_TRAY = 5; PLAY_TRACK = 132; STOP_TRACK = 133; RESUME_TRACK = 136; RESET_CD = 2; GET_TRACK_INFO = 11; GET_DISK_INFO = 10; READ_IOCTL = 3; WRITE_IOCTL = 12; MAX_CDS = 10; UPC_CODE = 14; LOCK_CODE = 1; LOCK_CD = 0; UNLOCK_CD = 1; Q_INFO = 12; MEDIA_CHANGE = 9; DEVICE_STATUS = 6; type rHRec = record length : byte; subUnitCode : byte; commandCode : byte; status : word; reserved : array[1..8] of byte; end; IOCTLRec = record r : rHRec; MDB : byte; tAOfs : word; tASeg : word; bytesToTransfer : word; startingSector : word; error : longint; end; TLockRec = record r : rHRec; code : byte; lock : byte; end; TUPCRec = record code : byte; control : byte; UPC : array[1..7] of byte; zero : byte; aFrame : byte; end; TPlayAudioTrackRec = record r : rHRec; mode : byte; start : longint; sectors : longint; end; TQRec = record code : byte; ADR : byte; Track : byte; Point : byte; min : byte; sec : byte; frame : byte; zero : byte; pMin : byte; pSec : byte; pFrame : byte; end; TMediaRec = record code : byte; media : byte; end; TStatusRec = record code : byte; status : longint; end; TTrackNameArray = array[1 .. MAX_TRACKS_PER_CD] of string[40]; TCDROMRec = record UPC : longint; cdName : string[40]; trackNames : TTrackNameArray; maxTrack : byte; end; TCDRackRec = record status : array[1 .. MAX_CDS] of word; rack : array[1 .. MAX_CDS] of TTrackArray; info : array[1 .. MAX_CDS] of TCDROMRec; end; var CDRack : TCDRackRec; result : word; startLetter : word; {--------------------------------------------------------------------} {Play A Track { This is a very simple CD player, which only plays the first { available CD, and only tracks 1-9. However, it does give { the idea. } procedure playATrack; var k : char; trk : word; e : integer; drive : word; quit : boolean; begin getNumberOfCDs(drive); getAllTrackDetails(drive); quit := FALSE; repeat repeat until keypressed; k := readKey; stopCD(drive); if (k >= '1') and (k <= '9') then begin write('Playing track : ',k,' '); val(k,trk,e); playTrack(drive,trk); end else quit := TRUE; until quit; end; {--------------------------------------------------------------------} { { { { } function getNumberOfCDs( var sLetter : word) : word; var drives, startLetter : word; begin asm mov ax,1500h int 2fh; mov startLetter,CX mov drives,BX end; sLetter := startLetter; getNumberOfCDs := drives; end; {--------------------------------------------------------------------} { { { { } procedure setIOCTL( var i : IOCTLRec ); var index : byte; begin with i do begin with r do begin length := 0; subUnitCode := 0; commandCode := 0; status := 0; for index := 1 to 8 do reserved[index] := 0; end;{with R} MDB := 0; tASeg := 0; tAOfs := 0; bytesToTransfer := 0; startingSector := 0; error := 0; end;{with i} end; {--------------------------------------------------------------------} { { { { } procedure getTrackDetails( drive : word; trk : byte; var t : TAudioTrackRec ); var index : word; i : IOCTLRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 7; tAOfs := ofs(t); tASeg := seg(t); end; with i.r do begin length := sizeof(i); commandCode := READ_IOCTL; end; with t do begin track := trk; code := GET_TRACK_INFO; end; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; { t will now contain the relevant information } end; {--------------------------------------------------------------------} { { { { } function getCDDetails( drive : word; var t : TAudioDiskRec ) : word; var index : word; i : IOCTLRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 7; tAOfs := ofs(t); tASeg := seg(t); end; with i.r do begin length := sizeof(i); commandCode := READ_IOCTL; end; with t do begin code := GET_DISK_INFO; lowTrack := 0; highTrack := 0; startPoint := 0; end; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; { t will now contain the relevant information } getCDDetails := i.r.status; end; {--------------------------------------------------------------------} { { { { } function redBookToSectors( red : longint ) : longint; var frame, minute, second : byte; temp : longint; begin frame := (red AND $000000FF); second := (red AND $0000FF00) shr 8; minute := (red AND $00FF0000) shr 16; temp := minute *60; temp := temp *75; temp := temp + second *75; temp := temp + frame -150; redBookToSectors := temp; end; {--------------------------------------------------------------------} { { { { } procedure getAllTrackDetails( drive : word ); var diskInfo : TAudioDiskRec; aTrack : TAudioTrackRec; firstCD : word; currentTrack : byte; index1, index2 : byte; pos1,pos2,pos3 : word; begin getCDDetails(drive,diskInfo); for currentTrack := diskInfo.lowTrack to diskInfo.highTrack do begin getTrackDetails(firstCD,currentTrack,aTrack); with tracks[currentTrack] do begin number := currentTrack; start := aTrack.startPoint; if (currentTrack > 1) then begin tracks[currentTrack-1].finish := tracks[currentTrack].start-1; end; end;{with} end;{for} end; {--------------------------------------------------------------------} { { { { } procedure stopCD(drive : word); var r : rHRec; s,o : word; begin r.commandCode := 133; r.length := sizeOf(r); s := seg(r); o := ofs(r); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure playTrack(drive,track : word); var index : word; p : TPlayAudioTrackRec; s,o : word; st,fi : longint; begin p.r.subUnitCode := 0; p.r.status := 0; p.r.commandcode := 132; p.r.length := sizeOf(p); for index := 1 to 8 do begin p.r.reserved[index] := 0; end; p.start := tracks[track].start; fi := redBookToSectors(tracks[track].finish); st := redBookToSectors(tracks[track].start); p.sectors := fi-st; p.mode := 1; s := seg(p); o := ofs(p); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure playTrack2(drive : word; start, finish : longint); var index : word; p : TPlayAudioTrackRec; s,o : word; st,fi : longint; begin p.r.subUnitCode := 0; p.r.status := 0; p.r.commandcode := 132; p.r.length := sizeOf(p); for index := 1 to 8 do begin p.r.reserved[index] := 0; end; p.start := start; fi := redBookToSectors(finish); st := redBookToSectors(start); p.sectors := fi-st; p.mode := 1; s := seg(p); o := ofs(p); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure ejectCD( drive : word ); var index : word; ejRec : record code : byte; end; i : IOCTLRec; rq : rHRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 1; tAOfs := ofs(ejRec); tASeg := seg(ejRec); end; with i.r do begin length := sizeof(i); commandCode := 12; end; ejRec.code := EJECT_TRAY; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure closeCD( drive : word ); var index : word; ejRec : record code : byte; end; i : IOCTLRec; rq : rHRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 1; tAOfs := ofs(ejRec); tASeg := seg(ejRec); end; with i.r do begin length := sizeof(i); commandCode := 12; end; ejRec.code := CLOSE_TRAY; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure resetCD( drive : word ); var index : word; resetRec : record code : byte; end; i : IOCTLRec; rq : rHRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 1; tAOfs := ofs(resetRec); tASeg := seg(resetRec); end; with i.r do begin length := sizeof(i); commandCode := 12; end; resetRec.code := RESET_CD; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure lockCD( drive : word ); var index : word; lockRec : record code : byte; lock : byte; end; i : IOCTLRec; rq : rHRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 2; tAOfs := ofs(lockRec); tASeg := seg(lockRec); end; with i.r do begin length := sizeof(i); commandCode := 12; end; lockRec.code := LOCK_CODE; lockRec.lock := LOCK_CD; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } procedure unlockCD( drive : word ); var index : word; lockRec : record code : byte; lock : byte; end; i : IOCTLRec; rq : rHRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 2; tAOfs := ofs(lockRec); tASeg := seg(lockRec); end; with i.r do begin length := sizeof(i); commandCode := 12; end; lockRec.code := LOCK_CODE; lockRec.lock := LOCK_CD; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; end; {--------------------------------------------------------------------} { { { { } function getUnique( drive : word ) : string; var a : TAudioDiskRec; s : string; begin getCDDetails(drive,a); Str(a.startPoint,s); getUnique := s; end; {--------------------------------------------------------------------} { { { { } procedure getUPC( drive : word; var UPC : string ); var u : TUPCRec; s,o : word; index : integer; res : byte; i : IOCTLRec; begin setIOCTL(i); with i do begin bytesToTransfer := 11; tAOfs := ofs(u); tASeg := seg(u); end; with i.r do begin length := 23{sizeof(i);}; commandCode := READ_IOCTL; end; u.code := UPC_CODE; u.control := 2; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; upc := ''; for index := 1 to 7 do begin res := (u.upc[index] AND $0F); upc := concat(upc,chr(res+48)); if (index < 7) then begin res := ((u.upc[index] AND $F0) SHR 4); upc := concat(upc,chr(res+48)); end; end;{for} end; {--------------------------------------------------------------------} { { { { } function getNumberOfTracks( drive : word ) : word; var a : TAudioDiskRec; begin getCDDetails(drive,a); getNumberOfTracks := a.highTrack; end; {--------------------------------------------------------------------} { { { { } procedure readVTOC( drive : word ); var buffer : array[1..2048] of byte; s,o : word; begin o := ofs(buffer); s := seg(buffer); asm mov ax,1508h; mov cx,drive; mov si,s ; mov di,o ; mov dx,1 ; int 2fh; end end; {--------------------------------------------------------------------} { { { { } procedure resumePlay( drive : word ); var r : rHRec; o,s : word; begin r.commandCode := RESUME_TRACK; r.length := 1; o := ofs(r); s := seg(r); asm mov ax,1510h; mov cx,drive; mov es,s ; mov bx,o ; int 2fh; end end; {--------------------------------------------------------------------} { { { { } function getRunningTime( drive : word; var min,sec : byte ) : word; var q : TQRec; index : word; i : IOCTLRec; rq : rHRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 1; tAOfs := ofs(q); tASeg := seg(q); end; with i.r do begin length := sizeof(i); commandCode := READ_IOCTL; end; q.code := Q_INFO; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; min := q.min; sec := q.sec; getRunningTime := i.r.status; end; {--------------------------------------------------------------------} { { { { } function mediaChanged( drive : word ) : boolean; var q : TMediaRec; index : word; i : IOCTLRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 2; tAOfs := ofs(q); tASeg := seg(q); end; with i.r do begin length := sizeof(i); commandCode := READ_IOCTL; end; q.code := MEDIA_CHANGE; q.media := 0; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; if (q.media < 1) then mediaChanged := TRUE else mediaChanged := FALSE; end; {--------------------------------------------------------------------} { { { { } function doorOpen( drive : word ) : boolean; var q : TStatusRec; index : word; i : IOCTLRec; s,o : word; begin setIOCTL(i); with i do begin bytesToTransfer := 5; tAOfs := ofs(q); tASeg := seg(q); end; with i.r do begin length := sizeof(i); commandCode := READ_IOCTL; end; q.code := DEVICE_STATUS; q.status := 0; s := seg(i); o := ofs(i); asm mov ax,1510h mov cx,drive mov es,S mov bx,O int 2fh end; if (q.status AND $01)=1 then doorOpen := TRUE else doorOpen := FALSE; end; {--------------------------------------------------------------------} { { { { } procedure init; var index : word; begin { initially we start this unit by reading all the available { CD-ROMs and taking in their track details. } for index := 1 to MAX_CDS do begin with CDRack do begin status[index] := 0; info[index].maxTrack := 0; end; end; end; {--------------------------------------------------------------------} { { { { } begin { initially we start this unit by reading all the available { CD-ROMs and taking in their track details. } init; end. { Paul L Daniels Software Development (DOS, OS/2) jackdan@ibm.net jackdan@ozemail.com.au }