[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]