798 lines
24 KiB
ObjectPascal
798 lines
24 KiB
ObjectPascal
{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.
|
|
|