{This unit is part of United Openlibraries of Sound (uos)} { From CDRom, CDromLinux, CDRomWindows By Franklyn A. Harlow Feb 2016 License : modified LGPL. Merged by Fred van Stappen fiens/hotmail.com } unit uos_cdrom; {$mode objfpc}{$H+} interface uses {$IFDEF FPC}cdrom,{$ENDIF} {$IFDEF MSWINDOWS} Windows, {$ENDIF} {$IFDEF unix} baseunix, {$ENDIF} Classes, SysUtils, math; Const CDROM_OK = 0; CDROM_UNKNOWNERR = -1; CD_FRAMESIZE_RAW = 2352; BUF_SIZE = 75 * CD_FRAMESIZE_RAW; // 75 frames - 1 sec kCDDA_Base = 'cdda://sr'; kCDDA_Track = 'Track%20'; kCDDA_TrackWin = 'Track'; Type TCDDATrackType = (cdtAudio, cdtData, cdtMixed); Tmsf = record min : Byte; sec : Byte; Frame : Byte; end; TCDDAPosition = record Track : LongInt; msf : Tmsf; end; TCDDATrackInfo = record TrackLength : Tmsf; TrackType : TCDDATrackType; end; TCDStatus = (cdsNotReady, cdsReady, cdsPlaying, cdsPaused); TCDDATOCEntry = record dwStartSector : LongInt; // Start sector of the track btFlag : Byte; // Track flags (i.e. data or audio track) btTrackNumber : Byte; // Track number end; PCDROMInfo = ^TCDROMInfo; TCDROMInfo = Record Channels : longword; // stereo = 2 BitsPerSample : longword; // ie short/smallint = 16 SampleRate : longword; // Frequency = 44100 TotalSamples : Int64; TotalTime : LongWord; // Seconds pData : pByte; pDataLen : longword; Size : Int64; Position : Int64; StartPos : TCDDAPosition; EndPos : TCDDAPosition; BufStart : LongWord; BufEnd : LongWord; BufSize : LongInt; Buf : array[1..BUF_SIZE*2] of Byte; BufTmp : array[1..BUF_SIZE*2] of Byte; fHandleVaild : longint; // 0< : Impossible, 0: Not Valid, >0 : Valid/Available CurrentPosition : Tmsf; // Current Position Of Track Being Read {$IFDEF LIBCDRIP} Opened : LongInt; Busy : Boolean; CurrentDrive : LongInt; EnableJitterCorrection : Boolean; LastJitterErrors : LongInt; ReadSectors : LongInt; OverlapSectors : LongInt; CompareSectors : LongInt; MultiReadCount : LongInt; Paranoid : Boolean; ParanoiaMode : LongInt; LockTray : Boolean; Status : TCDStatus; RipSize : LongInt; {$ENDIF} {$IFDEF MSWINDOWS} hCDROM : longword; // CDROM CreateFile Handle {$ENDIF} {$IFDEF unix} hCDROM : longint; // CDROM fpOpen Handle {$ENDIF} End; Const EndOfDisc : TCDDAPosition = (Track : 100; msf : (min : 0; sec : 0; frame : 0)); Function Frames2MSF(Frames : LongInt) : Tmsf; Function MSF2Frames(const msf : Tmsf) : LongInt; Function AddressToSectors (msf : Tmsf): int64; Function LoadCDROM(Lib : AnsiString): LongWord; Function UnloadCDROM: LongWord; Function isCDROMLoaded : Boolean; Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte; Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte; Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo; Function CDROM_Open(Drive, Track : Byte): PCDROMInfo; Function CDROM_Close(var pCDROMI: PCDROMInfo): LongWord; Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord; Function GetSystemCDRoms: AnsiString; implementation Function LoadCDROM(Lib : AnsiString): LongWord; Begin Result:= 32767; end; Function UnloadCDROM: LongWord; Begin Result:= 32767; end; Function isCDROMLoaded : Boolean; Begin Result:= True; end; {$IFDEF unix} Const CDROM_LBA = $01; // 'logical block': first frame is #0 CDROM_MSF = $02; // 'minute-second-frame': binary, not bcd here! CDROM_DATA_TRACK = $40; CDROM_LEADOUT = $AA; CDROMREADTOCHDR = $5305; // Read TOC header CDROMREADTOCENTRY = $5306; // Read TOC entry CDROMREADAUDIO = $530E; // (struct cdrom_read_audio) CDROM_DRIVE_STATUS = $5326; // Get tray position, etc. CDROM_DISC_STATUS = $5327; // Get disc type, etc. CDS_NO_DISC = 1; CDS_TRAY_OPEN = 2; CDS_DRIVE_NOT_READY = 3; CDS_DISC_OK = 4; CDS_AUDIO = 100; CDS_MIXED = 105; type cdrom_addr = record case Word of 1: (msf: Tmsf;); 2: (lba: longint;); end; cdrom_read_audio = record addr : cdrom_addr; // frame address addr_format : Byte; // CDROM_LBA or CDROM_MSF nframes : LongInt; // number of 2352-byte-frames to read at once buf : PByte; // frame buffer (size: nframes*2352 bytes) end; cdrom_tocentry = record cdte_track : Byte; cdte_adr_ctrl : Byte; cdte_format : Byte; cdte_addr : cdrom_addr; cdte_datamode : Byte; end; cdrom_tochdr = record cdth_trk0: Byte; // start track cdth_trk1: Byte; // end track end; // ********************** Private Linux SUPPORT Functions ************************************************************** function GetTrackInfo(hCDROM: LongInt; Track : LongInt): TCDDATrackInfo; var Entry : cdrom_tocentry; TOC : cdrom_tochdr; F1 : LongInt; F2 : LongInt; Ret : LongInt; begin Ret:= fpioctl(hCDROM, CDROMREADTOCHDR, @TOC); if Ret <> 0 then raise Exception.Create('mcwCDROM_Linux.GetTrackInfo.fpioctl(CDROMREADTOCHDR) Error : ' + IntToStr(Ret)); Entry.cdte_format := CDROM_MSF; Entry.cdte_track := Track + TOC.cdth_trk0 - 1; Ret:= fpioctl(hCDROM, CDROMREADTOCENTRY, @Entry); if Ret <> 0 then raise Exception.Create('mcwCDROM_Linux.GetTrackInfo.fpioctl(CDROMREADTOCENTRY) Error : ' + IntToStr(Ret)); F1 := MSF2Frames(Entry.cdte_addr.msf); if (Entry.cdte_adr_ctrl and CDROM_DATA_TRACK) <> 0 then Result.TrackType := cdtData else Result.TrackType := cdtAudio; if Entry.cdte_track < toc.cdth_trk1 then Inc(Entry.cdte_track) else Entry.cdte_track := CDROM_LEADOUT; Ret:= fpioctl(hCDROM, CDROMREADTOCENTRY, @Entry); if Ret <> 0 then raise Exception.Create('mcwCDROM_Linux.GetTrackInfo.fpioctl(CDROMREADTOCENTRY) #2 Error : ' + IntToStr(Ret)); F2 := MSF2Frames(Entry.cdte_addr.msf); Result.TrackLength:= Frames2MSF(F2 - F1); end; function GetTrackMSF(hCDROM, Track : LongInt): Tmsf; var entry : cdrom_tocentry; hdr : cdrom_tochdr; Ret : LongInt; begin Ret:= fpioctl(hCDROM, CDROMREADTOCHDR, @hdr); if Ret <> 0 then raise Exception.Create('mcwCDROM_Linux.GetTrackMSF.fpioctl(CDROMREADTOCHDR) Error : ' + IntToStr(Ret)); entry.cdte_format := CDROM_MSF; entry.cdte_track := Track + hdr.cdth_trk0 - 1; if entry.cdte_track > hdr.cdth_trk1 then entry.cdte_track := CDROM_LEADOUT; Ret:= fpioctl(hCDROM, CDROMREADTOCENTRY, @entry); if Ret <> 0 then raise Exception.Create('mcwCDROM_Linux.GetTrackMSF.fpioctl(CDROMREADTOCENTRY) Error : ' + IntToStr(Ret)); Result := entry.cdte_addr.msf; end; function GetPosMSF(hCDROM: LongInt; Pos : TCDDAPosition): Tmsf; var msf1 : Tmsf; frames : longint; begin msf1 := GetTrackMSF(hCDROM, Pos.Track); frames := MSF2Frames(msf1); frames := frames + MSF2Frames(Pos.msf); Result := Frames2MSF(frames); end; function GetSize(pCDROMI: PCDROMInfo): Int64; var F1 : LongWord; F2 : LongWord; begin F1:= (((pCDROMI^.StartPos.msf.min * 60) + pCDROMI^.StartPos.msf.sec) * 75) + pCDROMI^.StartPos.msf.Frame; F2:= (((pCDROMI^.EndPos.msf.min * 60) + pCDROMI^.EndPos.msf.sec) * 75) + pCDROMI^.EndPos.msf.Frame; Result := (F2 - F1) * CD_FRAMESIZE_RAW; end; // ********************************************************************************************************************* Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte; Begin Result:= 255; // Assume Error if Copy(FileName, 1, Length(kCDDA_Base)) = kCDDA_Base then Result:= StrToIntDef(FileName[10], 256); end; Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte; Var s : AnsiString; Begin Result:= 0; if Pos(kCDDA_Track, FileName) > 0 then Begin s:= Copy(FileName, Pos(kCDDA_Track, FileName) + Length(kCDDA_Track), Length(FileName)); s:= Copy(s, 1, Pos('.', s) -1); Result:= StrToIntDef(s, 0); end; end; Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo; Var Drive, Track : Byte; Begin Result:= CDROM_Open(CDROM_GetFileNameDrive(FileName), CDROM_GetFileNameTrack(FileName)); end; Function CDROM_Open(Drive, Track : Byte): PCDROMInfo; Var CDRomPath : AnsiString; slCDROMS : TStringList; Res : longint; Data : longint; CDTI : TCDDATrackInfo; CDTrackCount : LongInt; CDTOC : Array of TTocEntry; Begin Result:= nil; if (Drive = 255) or (Track < 1) then Exit; New(Result); // Read Only, CDROMI uses constants and ignores changes to these... Result^.Channels := 2; Result^.BitsPerSample:= 16; Result^.SampleRate := 44100; CDRomPath:= '/dev/sr' + IntToStr(Drive); slCDROMS := TStringList.Create; Try slCDROMS.Text:= GetSystemCDRoms; if slCDROMS.IndexOf(CDRomPath) = -1 then raise Exception.Create('mcwCDROM_Linux.CDROM_Open.GetSystemCDRoms Error : ' + CDRomPath + ' Not Found.'); finally slCDROMS.Free; end; Result^.hCDROM:= fpopen(PAnsiChar(CDRomPath), O_RDONLY or O_NONBLOCK);; if Result^.hCDROM < 0 then raise Exception.Create('mcwCDROM_Linux.CDROM_Open.fpopen Error : (' + IntToStr(Result^.hCDROM) + ') On ' + CDRomPath); // What State Is CDROM in ? Res:= fpioctl(Result^.hCDROM, CDROM_DRIVE_STATUS, @Data); if Res <> CDS_DISC_OK then Begin CDROM_Close(Result); Dispose(Result); Result:= nil; Exit; end; // CDRom OK, What Kind Of Disk Do We Have? Res := fpioctl(Result^.hCDROM, CDROM_DISC_STATUS, @Data); if (Res <> CDS_AUDIO) And (Res <> CDS_MIXED) Then Begin CDROM_Close(Result); Dispose(Result); Result:= nil; Exit; end; CDTI := GetTrackInfo(Result^.hCDROM, Track); if CDTI.TrackType = cdtData then raise Exception.Create('mcwCDROM_Linux.CDROM_Open : Trying to rip a data track'); Result^.StartPos.Track := Track; Result^.StartPos.msf := GetTrackMSF(Result^.hCDROM, Result^.StartPos.Track); Result^.EndPos.Track := Track +1; Result^.EndPos.msf := GetTrackMSF(Result^.hCDROM, Result^.EndPos.Track); CDTrackCount := cdrom.ReadCDTOC(CDRomPath, CDTOC); if (Result^.EndPos.Track in [1..CDTrackCount]) = False then raise Exception.Create('mcwCDROM_Linux.CDROM_Open : The end track out of range' + #13 + IntToStr(Result^.EndPos.Track) + ' Requested, 1..' + IntToStr(CDTrackCount) + ' Allowed...'); Result^.CurrentPosition := Result^.StartPos.msf; Result^.Position := 0; Result^.Size := GetSize(Result); Result^.BufStart := 1; Result^.BufEnd := 0; Result^.BufSize := BUF_SIZE; Result^.TotalSamples := Result^.Size div 4; Result^.TotalTime := floor(Result^.TotalSamples / 44100); Inc(Result^.fHandleVaild); end; Function CDROM_Close(var pCDROMI: PCDROMInfo): LongWord; Begin Result := CDROM_UNKNOWNERR; if pCDROMI^.fHandleVaild = 1 then fpclose(pCDROMI^.hCDROM); if pCDROMI^.fHandleVaild > 0 then Dec(pCDROMI^.fHandleVaild); Result := CDROM_OK; end; Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord; var ReqLen : LongWord; ReqFrames : LongInt; cdaudio : cdrom_read_audio; Ret : LongWord; Res : LongInt; TmpCount : longword; Procedure getNextChunk; Begin TmpCount:= 0; // Have We Reached End On Track ? if MSF2Frames(pCDROMI^.CurrentPosition) >= MSF2Frames(pCDROMI^.EndPos.msf) then Begin pCDROMI^.BufEnd := 0; pCDROMI^.BufStart := 0; Exit; End; // This is not first call if pCDROMI^.BufEnd > 0 then Begin // Copy Leftover Data to Start of buffer... TmpCount:= pCDROMI^.BufEnd - pCDROMI^.BufStart; Move(pCDROMI^.Buf[pCDROMI^.BufStart], pCDROMI^.BufTmp[1], TmpCount); Move(pCDROMI^.BufTmp[1], pCDROMI^.Buf[1], TmpCount); End; ReqFrames:= 75; // BUF_SIZE = 1 Sec Worth Data = 75 Frames = In Bytes if MSF2Frames(pCDROMI^.CurrentPosition) + ReqFrames > MSF2Frames(pCDROMI^.EndPos.msf) then ReqFrames:= MSF2Frames(pCDROMI^.EndPos.msf) - MSF2Frames(pCDROMI^.CurrentPosition); // *** Rip Next Chunk ****************************************** cdaudio.nframes := ReqFrames; // BUF_SIZE = 1 Sec Worth Data = 75 Frames = In Bytes cdaudio.addr_format := CDROM_MSF; cdaudio.addr.msf := pCDROMI^.CurrentPosition; cdaudio.buf := @pCDROMI^.Buf[TmpCount +1]; Res:= fpioctl(pCDROMI^.hCDROM, CDROMREADAUDIO, @cdaudio); if Res <> 0 then raise Exception.Create('mcwCDROM_Linux.CDROM_GetData.getNextChunk.fpioctl(CDROMREADAUDIO) Error : ' + IntToStr(fpgeterrno)); pCDROMI^.CurrentPosition:= Frames2MSF(MSF2Frames(pCDROMI^.CurrentPosition) + cdaudio.nframes); Ret := cdaudio.nframes * CD_FRAMESIZE_RAW; // *** End Rip Next Chunk *************************************** pCDROMI^.BufEnd:= TmpCount + Ret + 1; pCDROMI^.BufStart := 1; end; begin // PortAudio expects exact amount of data, anything less causes "Blips" // pCDROMI.BufStart = Start Byte of Current Valid, Not Sent Buffer... // pCDROMI.BufEnd = Last Byte of Current Valid, Not sent Buffer... ReqLen:= DataLength; if pCDROMI^.fHandleVaild = 0 then raise Exception.Create('mcwCDROM_Linux.CDROM_GetData Error : Call To GetData Without Vaild CDROM Handle.'); // We don't read CDROM every call, only when we need new data... if (pCDROMI^.BufStart + ReqLen) > pCDROMI^.BufEnd then getNextChunk; // is the amount in buffer less than what was requested... if DataLength > (pCDROMI^.BufEnd - pCDROMI^.BufStart + 1) then DataLength := pCDROMI^.BufEnd - pCDROMI^.BufStart + 1; // Have We Finished Reading Track ? if pCDROMI^.BufEnd = 0 then DataLength:= 0; pData:= @pCDROMI^.Buf[pCDROMI^.BufStart]; If DataLength > 0 Then Begin Inc(pCDROMI^.BufStart, DataLength); Inc(pCDROMI^.Position, DataLength); end; Result:= DataLength; end; {$ENDIF} ////////////////////// {$IFDEF MSWINDOWS} const CDDA = 2; IOCTL_CDROM_READ_TOC = $00024000; IOCTL_CDROM_RAW_READ = $0002403E; IOCTL_STORAGE_CHECK_VERIFY2 = $0002D0800; MAXIMUM_NUMBER_TRACKS = 100; CB_CDROMSECTOR = 2048; Type _TRACK_DATA = record Reserved : UCHAR; Control_and_Adr: UCHAR; TrackNumber : UCHAR; Reserved1 : UCHAR; Address : array[0..3] of UCHAR; end; TRACK_DATA = _TRACK_DATA; PTRACK_DATA = ^_TRACK_DATA; _CDROM_TOC = record Length : WORD; FirstTrack : UCHAR; LastTrack : UCHAR; TrackData : array[0..(MAXIMUM_NUMBER_TRACKS)-1] of TRACK_DATA; end; CDROM_TOC = _CDROM_TOC; PCDROM_TOC = ^_CDROM_TOC; RAW_READ_INFO = record DiskOffset : Int64; SectorCount : Cardinal; TrackMode : Cardinal; end; Function fpgeterrno(): LongWord; Begin Result:= GetLastOSError; end; // ********************** Private Windows SUPPORT Functions ************************************************************ function GetTrackMSF(Table: CDROM_TOC; Track : LongInt): Tmsf; begin Result.min := Table.TrackData[Track -1].Address[1]; Result.sec := Table.TrackData[Track -1].Address[2]; Result.Frame:= Table.TrackData[Track -1].Address[3]; end; function GetPosMSF(Table: CDROM_TOC; Pos : TCDDAPosition): Tmsf; var msf1 : Tmsf; frames : longint; begin msf1 := GetTrackMSF(Table, Pos.Track); frames := MSF2Frames(msf1); frames := frames + MSF2Frames(Pos.msf); Result := Frames2MSF(frames); end; function GetSize(pCDROMI: PCDROMInfo): Int64; var F1 : LongWord; F2 : LongWord; begin F1:= (((pCDROMI^.StartPos.msf.min * 60) + pCDROMI^.StartPos.msf.sec) * 75) + pCDROMI^.StartPos.msf.Frame; F2:= (((pCDROMI^.EndPos.msf.min * 60) + pCDROMI^.EndPos.msf.sec) * 75) + pCDROMI^.EndPos.msf.Frame; Result := (F2 - F1) * CD_FRAMESIZE_RAW; end; // ********************************************************************************************************************* Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte; Var driveLetter : Char; drive : Char; cdromcount : Byte; found : Boolean; Begin found := False; driveLetter:= FileName[1]; cdromcount := 255; For drive := 'A' to 'Z' do if GetDriveType(PChar(drive + ':\')) = DRIVE_CDROM then Begin Inc(cdromcount); if drive = driveLetter then Begin found:= True; break; end; end; if not found then raise Exception.Create('CDROM (BDROM) Drive Not Found !'); Result := cdromcount; end; Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte; Var s : AnsiString; Begin // This works on Win7... s := Copy(Filename, Pos(kCDDA_TrackWin, Filename) + 5, Length(Filename)); s := Copy(s, 1, Pos('.', s) -1); Result:= StrToIntDef(s , 0); end; Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo; Var Drive, Track : Byte; Begin Result:= CDROM_Open(CDROM_GetFileNameDrive(FileName), CDROM_GetFileNameTrack(FileName)); end; Function CDROM_Open(Drive, Track : Byte): PCDROMInfo; Var CDRomPath : AnsiString; Table : CDROM_TOC; BytesRead : LongWord; Ret : BOOL; flags : longword; // Index : LongInt; Begin Result:= nil; if (Drive = 255) or (Track < 1) then Exit; New(Result); // Read Only, CDROMI uses constants and ignores changes to these... Result^.Channels := 2; Result^.BitsPerSample:= 16; Result^.SampleRate := 44100; CDRomPath:= UpperCase('\\.\CDROM') + IntToStr(Drive); flags := longword(GENERIC_READ); Result^.hCDROM:= CreateFileA(PAnsiChar(CDRomPath), Flags, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 ); if (Result^.hCDROM = INVALID_HANDLE_VALUE) then raise Exception.Create('mcwCDROM_Win.CDROM_Open.CreateFileA Error : (' + IntToStr(Result^.hCDROM) + ') On ' + CDRomPath); // What State Is CDROM in ? Ret:= DeviceIoControl(Result^.hCDROM, IOCTL_STORAGE_CHECK_VERIFY2, nil, 0, nil, 0, BytesRead, nil); if Not Ret then Begin CDROM_Close(Result); Dispose(Result); Result:= nil; Exit; end; Ret:= DeviceIoControl(Result^.hCDROM, IOCTL_CDROM_READ_TOC, nil, 0, @Table, sizeof(Table), BytesRead, nil); if Ret = False then raise Exception.Create('mcwCDROM_Win.CDROM_Open.DeviceIoControl(IOCTL_CDROM_READ_TOC) Error '); Result^.StartPos.Track := Track; Result^.StartPos.msf := GetTrackMSF(Table, Result^.StartPos.Track); Result^.EndPos.Track := Track +1; Result^.EndPos.msf := GetTrackMSF(Table, Result^.EndPos.Track); Result^.CurrentPosition := Result^.StartPos.msf; Result^.Position := 0; Result^.Size := GetSize(Result); Result^.BufStart := 1; Result^.BufEnd := 0; Result^.BufSize := BUF_SIZE; Result^.TotalSamples := Result^.Size div 4; Result^.TotalTime := floor(Result^.TotalSamples / 44100); Inc(Result^.fHandleVaild); end; Function CDROM_Close(var pCDROMI: PCDROMInfo): LongWord; Begin Result := CDROM_UNKNOWNERR; if pCDROMI^.fHandleVaild = 1 then CloseHandle(pCDROMI^.hCDROM); if pCDROMI^.fHandleVaild > 0 then Dec(pCDROMI^.fHandleVaild); Result := CDROM_OK; end; Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord; var ReqLen : LongWord; ReqFrames : LongInt; Info : RAW_READ_INFO; BytesRead : LongWord; Address : Int64; Ret : LongWord; Res : BOOL; TmpCount : longword; Procedure getNextChunk; Begin TmpCount:= 0; // Have We Reached End On Track ? if MSF2Frames(pCDROMI^.CurrentPosition) >= MSF2Frames(pCDROMI^.EndPos.msf) then Begin pCDROMI^.BufEnd := 0; pCDROMI^.BufStart := 0; Exit; End; // This is not first call if pCDROMI^.BufEnd > 0 then Begin // Copy Leftover Data to Start of buffer... TmpCount:= pCDROMI^.BufEnd - pCDROMI^.BufStart; Move(pCDROMI^.Buf[pCDROMI^.BufStart], pCDROMI^.BufTmp[1], TmpCount); Move(pCDROMI^.BufTmp[1], pCDROMI^.Buf[1], TmpCount); End; // While Linux Can deal With 75 Frame Request, Windows Only 20 (?) ReqFrames:= 20; // BUF_SIZE = 1 Sec Worth Data = 75 Frames = In Bytes if MSF2Frames(pCDROMI^.CurrentPosition) + ReqFrames > MSF2Frames(pCDROMI^.EndPos.msf) then ReqFrames:= MSF2Frames(pCDROMI^.EndPos.msf) - MSF2Frames(pCDROMI^.CurrentPosition); // *** Rip Next Chunk ****************************************** Address:= AddressToSectors(pCDROMI^.CurrentPosition); Info.TrackMode := CDDA; Info.SectorCount:= ReqFrames; Info.DiskOffset := Address * CB_CDROMSECTOR; Res:= DeviceIoControl(pCDROMI^.hCDROM, IOCTL_CDROM_RAW_READ, @Info, sizeof(Info), @pCDROMI^.Buf[TmpCount +1], ReqFrames * CD_FRAMESIZE_RAW, BytesRead, nil); if Res = False then raise Exception.Create('mcwCDROM_Win.CDROM_GetData.getNextChunk.fpioctl(CDROMREADAUDIO) Error : ' + IntToStr(fpgeterrno)); pCDROMI^.CurrentPosition:= Frames2MSF(MSF2Frames(pCDROMI^.CurrentPosition) + ReqFrames); Ret := BytesRead; // Should Be The same as "ReqFrames * CD_FRAMESIZE_RAW" // *** End Rip Next Chunk *************************************** pCDROMI^.BufEnd:= TmpCount + Ret + 1; pCDROMI^.BufStart := 1; end; begin // PortAudio expects exact amount of data, anything less causes "Blips" // pCDROMI.BufStart = Start Byte of Current Valid, Not Sent Buffer... // pCDROMI.BufEnd = Last Byte of Current Valid, Not sent Buffer... ReqLen:= DataLength; if pCDROMI^.fHandleVaild = 0 then raise Exception.Create('mcwCDROM_Win.CDROM_GetData Error : Call To GetData Without Vaild CDROM Handle.'); // We don't read CDROM every call, only when we need new data... if (pCDROMI^.BufStart + ReqLen) > pCDROMI^.BufEnd then getNextChunk; // is the amount in buffer less than what was requested... if DataLength > (pCDROMI^.BufEnd - pCDROMI^.BufStart + 1) then DataLength := pCDROMI^.BufEnd - pCDROMI^.BufStart + 1; // Have We Finished Reading Track ? if pCDROMI^.BufEnd = 0 then DataLength:= 0; pData:= @pCDROMI^.Buf[pCDROMI^.BufStart]; If DataLength > 0 Then Begin Inc(pCDROMI^.BufStart, DataLength); Inc(pCDROMI^.Position, DataLength); end; Result:= DataLength; end; {$ENDIF} ///////////////////// Function GetSystemCDRoms: AnsiString; var Index : longint; Ret : LongInt; Devices : Array of string; sl : TStringList; begin Result:= ''; sl:= TStringList.Create; SetLength(Devices, 99); Ret:= cdrom.GetCDRomDevices(Devices); If Ret > 0 Then For Index := 0 To Ret -1 do sl.Add(Devices[Index]); Result:= sl.Text; sl.Free; end; // ********************** Common Linux/Windows SUPPORT Functions ******************************************************* Function Frames2MSF(Frames : LongInt) : Tmsf; var Temp : Integer; begin Temp := Frames div 75; Result.min := Temp div 60; Result.sec := Temp mod 60; Result.Frame := Frames mod 75; end; function MSF2Frames(const msf : Tmsf) : LongInt; begin Result := ((msf.min * 60) + msf.sec) * 75 + msf.Frame; end; Function AddressToSectors (msf : Tmsf): int64; begin Result:= MSF2Frames(msf) - 150; end; // ********************************************************************************************************************* end.