{ I noticed that more than a few people wanted to know how to program a CD player. I did too, once. So here is the fruit of my labor. The programming isn't all that good. I wrote it to be SMALL. There are quite a number of unused variables, procedures, etc. Just try them! I'd like to thank Kristian Leonhard for two of his procedures. Saved a big amount of space and bad computing. No messages about how I _should_ have coded something. If you can make it so much better DO IT. I recommend a file called MSCDFUNC.DOC which contains all of the info used in this program. EXCEPT for the stuff that Kristian Leonhard wrote. Programmed by: Patrick O'Malley, 1995 } {$X+} {I know, bad directive} Program CDPlayer; Uses Crt; Type Std = record {read and write} length : byte; subcode : byte; cmdcode : byte; status : word; res : array[1..8] of byte; {---Nothing special between READ and WRITE----} MedDesc : Byte; XFerAddr : Pointer; XFerbytes : Word; SSNum : Word; ReqPtr : Longint; end; Playreq = record {play} Length : Byte; SubCode : Byte; {0?} CmdCode : Byte; {132} Status : Word; Res : Array[1..8] of byte; AddrMode : Byte; StrtSec : Longint; numSect : Longint; end; Qinfo = record {QI} {uses IOCTLI header} CtlCode : Byte; {12} CTl_ADR : Byte; TNO : Byte; POINT : Byte; MIN : Byte; SEC : Byte; Frame : Byte; Zero : Byte; AMIN : Byte; ASEc : Byte; AFRAME : Byte; end; DiskInfo = record {DI} {uses IOCTLI header} CtlCode : Byte; {10} LowTrack : Byte; HiTrack : Byte; StrtSect : Longint; end; TrackInfo = record {TI} {uses IOCTLI header} CtlCode : Byte; TrackNum : Byte; TStart : Longint; TCI : Byte; end; Eject = record {EJ} {uses IOCTLO header} CtlCode : Byte; {0} end; RstDrive = record CtlCode : Byte; end; MedType = record CtlCode : Byte; MediaByte : Byte; end; OneTime = record Minute : Byte; Second : Byte; Frame : Byte; end; RedBookFormat = Record Frame : Byte; Second : Byte; Minute : Byte; Unused : Byte; End; AudioTrackType = Array[0..1,0..255] Of RedBookFormat; Var {I think some of these vars are unused ;) } StdHdr : Std; Play : Playreq; QI : Qinfo; DI : DiskInfo; TI : TrackInfo; EJ : Eject; Rst : RstDrive; Media : MedType; {---Universal variables---} TStart : Array[1..64] of OneTime; TLength : Array[1..64] of OneTime; StdOfs : Word; CDNum : Word; NumDrives : Word; {number of CD-ROM devices found} TrNum : Byte; DrvList : Array[1..26] of byte; {list of CD-ROM devices} Tracks : AudioTrackType; Function LongMul(X,Y : Integer) : Longint; Assembler; Asm mov ax,X imul Y End; Procedure SendDDR; {Sends a device driver request. In this case to the CD-ROM driver} Begin asm push ds mov ax,ds mov es,ax mov ax,1510h mov bx,StdOfs mov cx,CDNum Int 2fh pop ds end; end; {sendDDR} Function IsInstalled : Boolean; Begin asm mov ax,1500h int 2fh mov numdrives,bx end; If Numdrives = 0 then IsInstalled := False else IsInstalled := True; End; {IsInstalled func} Function MediaChanged : Byte; Begin FillChar(StdHdr,SizeOf(StdHdr),0); StdHdr.Length := SizeOf(StdHdr); StdHdr.CmdCode := 3; StdHdr.XFerAddr := Addr(Media); StdHdr.XFerBytes := 2; Media.CtlCode := 9; SendDDR; MediaChanged := Media.MediaByte; end; Procedure GetDrvLetrs; {Gets the list of CD-ROM devices in the system. It returns a number for each drive. For example, if drive 'D' is a CD-ROM and the only one in the system, DrvList[1] would equal 3. Remember: A=0,B=1,C=2,D=3. DrvList[x] can be used to play any cd device. DrvList[2] would be the second device,etc.} Begin asm mov ax,ds mov es,ax mov ax,150dh mov bx,offset drvlist int 2fh end; end; {getdrvletrs proc} Procedure GetDriverName(CDROMNumber:Byte;Var Name:String); { Also from Kristian Leonhard. Interesting procedure. } { CDROMNumber = 1 .. 26 } { 1 means first cdrom number } Var Data : Array[0..129] Of Byte; Offs : Word; Segm : Word; DSeg : Word; DOfs : Word; Z : Word; Begin Offs:=Ofs(Data); Segm:=Seg(Data); Asm mov ax,segm mov es,ax mov bx,offs mov ax,1501h int 2fh End; DSeg:=MemW[segm:(Offs+3)]; DOfs:=MemW[segm:(Offs+1)]+((CDROMNumber-1)*5); Z:=0; While (Mem[DSeg:DOfs+Z]<>$20) And (Z<7) Do Begin Inc(Z); Mem[Seg(Name):Ofs(Name)]:=Z; Mem[Seg(Name):Ofs(Name)+Z]:=Mem[DSeg:(DOfs+9+Z)]; End; Inc(Z); Mem[Seg(Name):Ofs(Name)]:=Z; Mem[Seg(Name):Ofs(Name)+Z]:=0; End; {getdrivername proc} Procedure GetAudioTrackInfo(Handle:Word;Var AudioTrack:AudioTrackType); {Thanks to Kristian Leonhard for this procedure. Sure beats my old one! } Label Error,Exit,Error2,Exit2,Error3,Exit3; Var Buffer : Array[0..1024] Of Byte; BufSeg : Word; BufOfs : Word; Err : Boolean; Z : Byte; Max,Min: Byte; Procedure WriteTimeFromSec(Sec:Word); Begin Writeln((Sec div 60),':',Sec-((Sec div 60)*60)); End; Begin BufSeg:=Seg(Buffer); BufOfs:=Ofs(Buffer); { Get StartTrack & EndTrack } Asm push ds mov ax,BufSeg mov ds,ax mov dx,BufOfs mov bx,dx mov al,0ah mov ds:[bx],al mov bx,Handle mov cx,7 mov ax,4402h int 21h jc Error mov al,0h mov Err,al jmp Exit Error : mov al,1h mov Err,al jmp Exit Exit : pop ds End; If Not Err Then Begin Max:=Buffer[2]; Min:=Buffer[1]; AudioTrack[1][Max].Frame:=Buffer[3]; AudioTrack[1][Max].Second:=Buffer[4]; AudioTrack[1][Max].Minute:=Buffer[5]; { Get Rest of audio info } For Z:=Min To Max Do Begin Asm push ds mov ax,BufSeg mov ds,ax mov dx,BufOfs mov bx,dx mov al,0bh mov ds:[bx],al mov al,z mov ds:[bx+1],al mov bx,Handle mov cx,7 mov ax,4402h int 21h jc Error mov al,0h mov Err,al jmp Exit2 Error2 : mov al,1h mov Err,al jmp Exit2 Exit2 : pop ds End; AudioTrack[0][Z].Frame:=Buffer[2]; AudioTrack[0][Z].Second:=Buffer[3]; AudioTrack[0][Z].Minute:=Buffer[4]; End; For Z:=Min+1 To Max Do Begin Asm push ds mov ax,BufSeg mov ds,ax mov dx,BufOfs mov bx,dx mov al,0bh mov ds:[bx],al mov al,z mov ds:[bx+1],al mov bx,Handle mov cx,7 mov ax,4402h int 21h jc Error mov al,0h mov Err,al jmp Exit3 Error3 : mov al,1h mov Err,al jmp Exit3 Exit3 : pop ds End; AudioTrack[1][Z-1].Frame:=Buffer[2]; AudioTrack[1][Z-1].Second:=Buffer[3]; AudioTrack[1][Z-1].Minute:=Buffer[4]; End; Writeln('AudioCD Info : '); Writeln('Start End Total'); For Z:=Min To Max Do Begin Write(AudioTrack[0][Z].Minute,':',AudioTrack[0][Z].Second,' ', AudioTrack[1][Z].Minute,':',AudioTrack[1][Z].Second,' '); WriteTimeFromSec(((AudioTrack[1][Z].Minute-AudioTrack[0][Z].Minute) *60)+(AudioTrack[1][Z].Second-AudioTrack[0][Z].Second)); End; End Else Begin Writeln('Error reading audio info'); End; Writeln('Press a key'); Readkey; End; {getaudiotrackinfo proc} Procedure SubtractTime(Min1,Sec1,F1,Min2,Sec2,F2 : Byte;Var RMin,RSec,RF : Byte);{Subtracts Min1,Sec1 from Min2,Sec2 (Min2-Min1,etc)} {Places answers in RMin and RSec} Var D : Real; S1,S2,R : Word; Begin {Check to see if F2 > F1. If not, subtract one from S2, add 75 to F2, subtract and place result. then convert rest to packed} If F2-F1 < 0 then begin S2 := S2 - 1; F2 := F2 + 75; RF := F2-F1; end else RF := F2-F1; {recent addition -- check} {convert to packed} S1 := Min1*60+Sec1; S2 := Min2*60+Sec2; {do subtraction} R := S2-S1; {Is R > 60, if so then divide r by 60 to find RMin} {mult. frac(r) by 60 to fin rsec} If R >= 60 then begin D := R / 60; RMin := Trunc(D); D := Frac(D) * 60; RSec := Trunc(D); end else begin RMin := 0; RSec := R; End; End; {SubtractTime} Procedure EjectCd; Begin StdHdr.Length := SizeOf(StdHdr); StdHdr.Subcode := 0; StdHdr.CmdCode := 12; StdHdr.MedDesc := 0; StdHdr.XFerAddr := ADDR(EJ); StdHdr.XFerBytes := 1; StdHdr.SSNum := 0; StdHdr.ReqPtr := 0; EJ.CtlCode := 0; SendDDR; End; {ejectcd proc} Procedure ResetCD; Begin StdHdr.Length := SizeOf(StdHdr); StdHdr.Subcode := 0; StdHdr.CmdCode := 12; StdHdr.MedDesc := 0; StdHdr.XFerAddr := ADDR(RST); StdHdr.XFerBytes := 1; StdHdr.SSNum := 0; StdHdr.ReqPtr := 0; Rst.CtlCode := 2; SendDDR; End; {resetcd proc} Procedure GetDiskInfo; Begin StdHdr.Length := SizeOf(StdHdr); StdHdr.SubCode := 0; StdHdr.CmdCode := 3; StdHdr.MedDesc := 0; StdHdr.XFerAddr := Addr(DI); StdHdr.XFerBytes := 7; StdHdr.SSNum := 0; Stdhdr.ReqPtr := 0; DI.CtlCode := 10; SendDDR; End; {getdiskinfo proc} Procedure GetTrackInfo(Track : Byte;Var Start : Longint); Begin StdHdr.Length := SizeOf(StdHdr); StdHdr.SubCode := 0; StdHdr.CmdCode := 3; Stdhdr.MedDesc := 0; Stdhdr.XFerAddr := Addr(TI); StdHdr.XFerBytes := 7; StdHdr.SSNum := 0; StdHdr.ReqPtr := 0; TI.CtlCode := 11; TI.TrackNum := Track; SendDDR; Start := TI.TStart; End; {GetTrackInfo Proc} Procedure GetTrackLengths; Var Loop : Byte; Begin {place track starts in TStart} For Loop := DI.LowTrack to DI.HiTrack do Begin TStart[Loop].Minute := Tracks[0,Loop].Minute; TStart[Loop].Second := Tracks[0,Loop].Second; TStart[Loop].Frame := Tracks[0,Loop].Frame; end; {find lengths of tracks} For Loop := DI.LowTrack to DI.HiTrack do SubtractTime(Tracks[0,Loop].Minute,Tracks[0,Loop].Second,Tracks[0,Loop].Frame, Tracks[1,Loop].Minute,Tracks[1,Loop].Second,Tracks[1,Loop].Frame, TLength[Loop].Minute,TLength[Loop].Second,TLength[Loop].Frame);End; {gettracklengths proc} Procedure PlayCD(Start,Length : Longint); Begin Play.Length := 13{SizeOf(Play)}; Play.SubCode := 0; Play.CMdCode := 132; Play.AddrMode := 0; Play.StrtSec := Start; Play.NumSect := Length; asm push ds mov ax,ds mov es,ax mov ax,1510h mov bx,offset play mov cx,CDNum Int 2fh pop ds end; End; {playcd proc} Procedure PlayTrack(TrackNum : Byte); Var LMin,LSec,LF, SMin,SSec,SF : Byte; S,L : Longint; Begin LMin := TLength[TrackNum].Minute; LSec := TLength[TrackNum].Second; LF := TLength[TrackNum].Frame; SMin := TStart[TrackNum].Minute; SSec := TStart[Tracknum].Second; SF := TStart[TrackNum].Frame; L := LongMul(LMin,75*60)+LongMul(LSec,75)+LF-150; S := LongMul(SMin,60*75)+LongMul(SSec,75)+SF-150; PlayCD(S,L); End; {playtrack proc} Procedure PlayTracks(First,Second : Byte); {Plays everything from the first to the second tracks. Not used.} Var Strt : OneTime; X : Byte; LngthTime : Longint; L,S : Longint; Begin LngthTime := 0; Strt := TStart[First]; For X := First to Second do LngthTime := LngthTime + LongMul(TLength[X].Minute,75*60)+LongMul(TLength[X].Second,75)+ TLength[X].Frame-150; L := LngthTime; S := LongMul(Strt.Minute,60*75)+LongMul(Strt.Second,75)+Strt.Frame-150; PlayCD(S,L); End; {playtracks proc} Procedure GetAudioInfo; Var CDName : String; CDHandle : Word; CDFile : File; Begin GetDriverName(1,CDName); Assign(CDFile,CDName); Reset(CDFile,1); CDHandle := MemW[SSeg:Ofs(CDFile)]; GetAudioTrackInfo(CDHandle,Tracks); Close(CDFile); End; {getaudioinfo} Begin {--Set some universal constants--} StdOfs := Ofs(StdHdr); If IsInstalled then begin GetDrvLetrs; CDNum := DrvList[1]; End else Begin Writeln('MSCDEX and/or CD-ROM not detected! Aborting'); Halt(1); End; {--reset drive then play a track--} ResetCD; {My CD won't play music CDs without a reset first. Driver prob.} MediaChanged; {have to run it once to get a correct answer the next time} GetAudioInfo; { not that I use it here again ;)} GetDiskInfo; GetTrackLengths; {$I-} Repeat Writeln('Enter track number to play (',DI.LowTrack,'-',DI.HiTrack,'):'); Readln(TrNum); Until (IOResult = 0) AND (TrNum <= DI.HiTrack) AND (TrNum >= DI.LowTrack); {$I+} PlayTrack(TrNum); End. A word about CDNum: CDNum is the number of the drive where A=0,B=1,C=2,D=3. On my system the CD is drive D so CDNum would be 3. HOWEVER: In the GetDriverName procedure, CDROMNumber is NOT the same. It is the number of the CDROM drive you want to play. That is, if you have more than 1 CDROM. So if you have 2 CDROM drives, D&E, then CDROMNumber = 1 would activate drive D and CDROMNumber=2 would activate E. Simple. Keep it on 1 and things _should_ go OK. Pat O'Malley AKA Silicon Slim