1 | {This unit is part of United Openlibraries of Sound (uos)}
|
---|
2 | {
|
---|
3 | From CDRom, CDromLinux, CDRomWindows
|
---|
4 | By Franklyn A. Harlow Feb 2016
|
---|
5 | License : modified LGPL.
|
---|
6 | Merged by Fred van Stappen fiens/hotmail.com }
|
---|
7 |
|
---|
8 | unit uos_cdrom;
|
---|
9 |
|
---|
10 | {$mode objfpc}{$H+}
|
---|
11 |
|
---|
12 | interface
|
---|
13 |
|
---|
14 | uses
|
---|
15 | {$IFDEF FPC}cdrom,{$ENDIF}
|
---|
16 | {$IFDEF MSWINDOWS}
|
---|
17 | Windows,
|
---|
18 | {$ENDIF}
|
---|
19 | {$IFDEF unix}
|
---|
20 | baseunix,
|
---|
21 | {$ENDIF}
|
---|
22 | Classes, SysUtils, math;
|
---|
23 |
|
---|
24 | Const
|
---|
25 | CDROM_OK = 0;
|
---|
26 | CDROM_UNKNOWNERR = -1;
|
---|
27 |
|
---|
28 | CD_FRAMESIZE_RAW = 2352;
|
---|
29 | BUF_SIZE = 75 * CD_FRAMESIZE_RAW; // 75 frames - 1 sec
|
---|
30 |
|
---|
31 | kCDDA_Base = 'cdda://sr';
|
---|
32 | kCDDA_Track = 'Track%20';
|
---|
33 | kCDDA_TrackWin = 'Track';
|
---|
34 |
|
---|
35 | Type
|
---|
36 | TCDDATrackType = (cdtAudio, cdtData, cdtMixed);
|
---|
37 | Tmsf = record
|
---|
38 | min : Byte;
|
---|
39 | sec : Byte;
|
---|
40 | Frame : Byte;
|
---|
41 | end;
|
---|
42 | TCDDAPosition = record
|
---|
43 | Track : LongInt;
|
---|
44 | msf : Tmsf;
|
---|
45 | end;
|
---|
46 | TCDDATrackInfo = record
|
---|
47 | TrackLength : Tmsf;
|
---|
48 | TrackType : TCDDATrackType;
|
---|
49 | end;
|
---|
50 | TCDStatus = (cdsNotReady, cdsReady, cdsPlaying, cdsPaused);
|
---|
51 | TCDDATOCEntry = record
|
---|
52 | dwStartSector : LongInt; // Start sector of the track
|
---|
53 | btFlag : Byte; // Track flags (i.e. data or audio track)
|
---|
54 | btTrackNumber : Byte; // Track number
|
---|
55 | end;
|
---|
56 |
|
---|
57 | PCDROMInfo = ^TCDROMInfo;
|
---|
58 | TCDROMInfo = Record
|
---|
59 |
|
---|
60 | Channels : longword; // stereo = 2
|
---|
61 | BitsPerSample : longword; // ie short/smallint = 16
|
---|
62 | SampleRate : longword; // Frequency = 44100
|
---|
63 | TotalSamples : Int64;
|
---|
64 | TotalTime : LongWord; // Seconds
|
---|
65 | pData : pByte;
|
---|
66 | pDataLen : longword;
|
---|
67 | Size : Int64;
|
---|
68 | Position : Int64;
|
---|
69 | StartPos : TCDDAPosition;
|
---|
70 | EndPos : TCDDAPosition;
|
---|
71 | BufStart : LongWord;
|
---|
72 | BufEnd : LongWord;
|
---|
73 | BufSize : LongInt;
|
---|
74 | Buf : array[1..BUF_SIZE*2] of Byte;
|
---|
75 | BufTmp : array[1..BUF_SIZE*2] of Byte;
|
---|
76 | fHandleVaild : longint; // 0< : Impossible, 0: Not Valid, >0 : Valid/Available
|
---|
77 | CurrentPosition : Tmsf; // Current Position Of Track Being Read
|
---|
78 | {$IFDEF LIBCDRIP}
|
---|
79 | Opened : LongInt;
|
---|
80 | Busy : Boolean;
|
---|
81 | CurrentDrive : LongInt;
|
---|
82 | EnableJitterCorrection : Boolean;
|
---|
83 | LastJitterErrors : LongInt;
|
---|
84 | ReadSectors : LongInt;
|
---|
85 | OverlapSectors : LongInt;
|
---|
86 | CompareSectors : LongInt;
|
---|
87 | MultiReadCount : LongInt;
|
---|
88 | Paranoid : Boolean;
|
---|
89 | ParanoiaMode : LongInt;
|
---|
90 | LockTray : Boolean;
|
---|
91 | Status : TCDStatus;
|
---|
92 | RipSize : LongInt;
|
---|
93 | {$ENDIF}
|
---|
94 | {$IFDEF MSWINDOWS}
|
---|
95 | hCDROM : longword; // CDROM CreateFile Handle
|
---|
96 | {$ENDIF}
|
---|
97 | {$IFDEF unix}
|
---|
98 | hCDROM : longint; // CDROM fpOpen Handle
|
---|
99 | {$ENDIF}
|
---|
100 | End;
|
---|
101 |
|
---|
102 | Const
|
---|
103 | EndOfDisc : TCDDAPosition = (Track : 100; msf : (min : 0; sec : 0; frame : 0));
|
---|
104 |
|
---|
105 | Function Frames2MSF(Frames : LongInt) : Tmsf;
|
---|
106 | Function MSF2Frames(const msf : Tmsf) : LongInt;
|
---|
107 | Function AddressToSectors (msf : Tmsf): int64;
|
---|
108 |
|
---|
109 | Function LoadCDROM(Lib : AnsiString): LongWord;
|
---|
110 | Function UnloadCDROM: LongWord;
|
---|
111 | Function isCDROMLoaded : Boolean;
|
---|
112 |
|
---|
113 | Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte;
|
---|
114 | Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte;
|
---|
115 |
|
---|
116 | Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo;
|
---|
117 | Function CDROM_Open(Drive, Track : Byte): PCDROMInfo;
|
---|
118 | Function CDROM_Close(var pCDROMI: PCDROMInfo): Integer;
|
---|
119 | Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord;
|
---|
120 |
|
---|
121 | Function GetSystemCDRoms: AnsiString;
|
---|
122 |
|
---|
123 | implementation
|
---|
124 |
|
---|
125 | Function LoadCDROM(Lib : AnsiString): LongWord;
|
---|
126 | Begin
|
---|
127 | Result:= 32767;
|
---|
128 | end;
|
---|
129 |
|
---|
130 | Function UnloadCDROM: LongWord;
|
---|
131 | Begin
|
---|
132 | Result:= 32767;
|
---|
133 | end;
|
---|
134 |
|
---|
135 | Function isCDROMLoaded : Boolean;
|
---|
136 | Begin
|
---|
137 | Result:= True;
|
---|
138 | end;
|
---|
139 |
|
---|
140 | {$IFDEF unix}
|
---|
141 | Const
|
---|
142 | CDROM_LBA = $01; // 'logical block': first frame is #0
|
---|
143 | CDROM_MSF = $02; // 'minute-second-frame': binary, not bcd here!
|
---|
144 | CDROM_DATA_TRACK = $40;
|
---|
145 | CDROM_LEADOUT = $AA;
|
---|
146 |
|
---|
147 | CDROMREADTOCHDR = $5305; // Read TOC header
|
---|
148 | CDROMREADTOCENTRY = $5306; // Read TOC entry
|
---|
149 | CDROMREADAUDIO = $530E; // (struct cdrom_read_audio)
|
---|
150 | CDROM_DRIVE_STATUS = $5326; // Get tray position, etc.
|
---|
151 | CDROM_DISC_STATUS = $5327; // Get disc type, etc.
|
---|
152 |
|
---|
153 | CDS_NO_DISC = 1;
|
---|
154 | CDS_TRAY_OPEN = 2;
|
---|
155 | CDS_DRIVE_NOT_READY = 3;
|
---|
156 | CDS_DISC_OK = 4;
|
---|
157 |
|
---|
158 | CDS_AUDIO = 100;
|
---|
159 | CDS_MIXED = 105;
|
---|
160 |
|
---|
161 | type
|
---|
162 | cdrom_addr = record
|
---|
163 | case Word of
|
---|
164 | 1: (msf: Tmsf;);
|
---|
165 | 2: (lba: longint;);
|
---|
166 | end;
|
---|
167 | cdrom_read_audio = record
|
---|
168 | addr : cdrom_addr; // frame address
|
---|
169 | addr_format : Byte; // CDROM_LBA or CDROM_MSF
|
---|
170 | nframes : LongInt; // number of 2352-byte-frames to read at once
|
---|
171 | buf : PByte; // frame buffer (size: nframes*2352 bytes)
|
---|
172 | end;
|
---|
173 | cdrom_tocentry = record
|
---|
174 | cdte_track : Byte;
|
---|
175 | cdte_adr_ctrl : Byte;
|
---|
176 | cdte_format : Byte;
|
---|
177 | cdte_addr : cdrom_addr;
|
---|
178 | cdte_datamode : Byte;
|
---|
179 | end;
|
---|
180 | cdrom_tochdr = record
|
---|
181 | cdth_trk0: Byte; // start track
|
---|
182 | cdth_trk1: Byte; // end track
|
---|
183 | end;
|
---|
184 |
|
---|
185 | // ********************** Private Linux SUPPORT Functions **************************************************************
|
---|
186 | function GetTrackInfo(hCDROM: LongInt; Track : LongInt): TCDDATrackInfo;
|
---|
187 | var
|
---|
188 | Entry : cdrom_tocentry;
|
---|
189 | TOC : cdrom_tochdr;
|
---|
190 | F1 : LongInt;
|
---|
191 | F2 : LongInt;
|
---|
192 | Ret : LongInt;
|
---|
193 | begin
|
---|
194 | Ret:= fpioctl(hCDROM, CDROMREADTOCHDR, @TOC);
|
---|
195 | if Ret <> 0 then
|
---|
196 | raise Exception.Create('mcwCDROM_Linux.GetTrackInfo.fpioctl(CDROMREADTOCHDR) Error : ' + IntToStr(Ret));
|
---|
197 |
|
---|
198 | Entry.cdte_format := CDROM_MSF;
|
---|
199 | Entry.cdte_track := Track + TOC.cdth_trk0 - 1;
|
---|
200 |
|
---|
201 | Ret:= fpioctl(hCDROM, CDROMREADTOCENTRY, @Entry);
|
---|
202 | if Ret <> 0 then
|
---|
203 | raise Exception.Create('mcwCDROM_Linux.GetTrackInfo.fpioctl(CDROMREADTOCENTRY) Error : ' + IntToStr(Ret));
|
---|
204 |
|
---|
205 | F1 := MSF2Frames(Entry.cdte_addr.msf);
|
---|
206 |
|
---|
207 | if (Entry.cdte_adr_ctrl and CDROM_DATA_TRACK) <> 0 then
|
---|
208 | Result.TrackType := cdtData
|
---|
209 | else
|
---|
210 | Result.TrackType := cdtAudio;
|
---|
211 |
|
---|
212 | if Entry.cdte_track < toc.cdth_trk1 then
|
---|
213 | Inc(Entry.cdte_track)
|
---|
214 | else
|
---|
215 | Entry.cdte_track := CDROM_LEADOUT;
|
---|
216 |
|
---|
217 | Ret:= fpioctl(hCDROM, CDROMREADTOCENTRY, @Entry);
|
---|
218 | if Ret <> 0 then
|
---|
219 | raise Exception.Create('mcwCDROM_Linux.GetTrackInfo.fpioctl(CDROMREADTOCENTRY) #2 Error : ' + IntToStr(Ret));
|
---|
220 |
|
---|
221 | F2 := MSF2Frames(Entry.cdte_addr.msf);
|
---|
222 | Result.TrackLength:= Frames2MSF(F2 - F1);
|
---|
223 | end;
|
---|
224 |
|
---|
225 | function GetTrackMSF(hCDROM, Track : LongInt): Tmsf;
|
---|
226 | var
|
---|
227 | entry : cdrom_tocentry;
|
---|
228 | hdr : cdrom_tochdr;
|
---|
229 | Ret : LongInt;
|
---|
230 | begin
|
---|
231 | Ret:= fpioctl(hCDROM, CDROMREADTOCHDR, @hdr);
|
---|
232 | if Ret <> 0 then
|
---|
233 | raise Exception.Create('mcwCDROM_Linux.GetTrackMSF.fpioctl(CDROMREADTOCHDR) Error : ' + IntToStr(Ret));
|
---|
234 |
|
---|
235 | entry.cdte_format := CDROM_MSF;
|
---|
236 | entry.cdte_track := Track + hdr.cdth_trk0 - 1;
|
---|
237 | if entry.cdte_track > hdr.cdth_trk1 then
|
---|
238 | entry.cdte_track := CDROM_LEADOUT;
|
---|
239 |
|
---|
240 | Ret:= fpioctl(hCDROM, CDROMREADTOCENTRY, @entry);
|
---|
241 | if Ret <> 0 then
|
---|
242 | raise Exception.Create('mcwCDROM_Linux.GetTrackMSF.fpioctl(CDROMREADTOCENTRY) Error : ' + IntToStr(Ret));
|
---|
243 |
|
---|
244 | Result := entry.cdte_addr.msf;
|
---|
245 | end;
|
---|
246 |
|
---|
247 | function GetPosMSF(hCDROM: LongInt; Pos : TCDDAPosition): Tmsf;
|
---|
248 | var
|
---|
249 | msf1 : Tmsf;
|
---|
250 | frames : longint;
|
---|
251 | begin
|
---|
252 | msf1 := GetTrackMSF(hCDROM, Pos.Track);
|
---|
253 | frames := MSF2Frames(msf1);
|
---|
254 | frames := frames + MSF2Frames(Pos.msf);
|
---|
255 | Result := Frames2MSF(frames);
|
---|
256 | end;
|
---|
257 |
|
---|
258 | function GetSize(pCDROMI: PCDROMInfo): Int64;
|
---|
259 | var
|
---|
260 | F1 : LongWord;
|
---|
261 | F2 : LongWord;
|
---|
262 | begin
|
---|
263 | F1:= (((pCDROMI^.StartPos.msf.min * 60) + pCDROMI^.StartPos.msf.sec) * 75) + pCDROMI^.StartPos.msf.Frame;
|
---|
264 | F2:= (((pCDROMI^.EndPos.msf.min * 60) + pCDROMI^.EndPos.msf.sec) * 75) + pCDROMI^.EndPos.msf.Frame;
|
---|
265 | Result := (F2 - F1) * CD_FRAMESIZE_RAW;
|
---|
266 | end;
|
---|
267 | // *********************************************************************************************************************
|
---|
268 |
|
---|
269 | Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte;
|
---|
270 | Begin
|
---|
271 | Result:= 255; // Assume Error
|
---|
272 | if Copy(FileName, 1, Length(kCDDA_Base)) = kCDDA_Base then
|
---|
273 | Result:= StrToIntDef(FileName[10], 256);
|
---|
274 | end;
|
---|
275 |
|
---|
276 | Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte;
|
---|
277 | Var
|
---|
278 | s : AnsiString;
|
---|
279 | Begin
|
---|
280 | Result:= 0;
|
---|
281 | if Pos(kCDDA_Track, FileName) > 0 then
|
---|
282 | Begin
|
---|
283 | s:= Copy(FileName, Pos(kCDDA_Track, FileName) + Length(kCDDA_Track), Length(FileName));
|
---|
284 | s:= Copy(s, 1, Pos('.', s) -1);
|
---|
285 | Result:= StrToIntDef(s, 0);
|
---|
286 | end;
|
---|
287 | end;
|
---|
288 |
|
---|
289 | Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo;
|
---|
290 | Var
|
---|
291 | Drive, Track : Byte;
|
---|
292 | Begin
|
---|
293 | Result:= CDROM_Open(CDROM_GetFileNameDrive(FileName),
|
---|
294 | CDROM_GetFileNameTrack(FileName));
|
---|
295 | end;
|
---|
296 |
|
---|
297 | Function CDROM_Open(Drive, Track : Byte): PCDROMInfo;
|
---|
298 | Var
|
---|
299 | CDRomPath : AnsiString;
|
---|
300 | slCDROMS : TStringList;
|
---|
301 |
|
---|
302 | Res : longint;
|
---|
303 | Data : longint;
|
---|
304 | CDTI : TCDDATrackInfo;
|
---|
305 | CDTrackCount : LongInt;
|
---|
306 | CDTOC : Array of TTocEntry;
|
---|
307 | Begin
|
---|
308 | Result:= nil;
|
---|
309 | if (Drive = 255) or (Track < 1) then
|
---|
310 | Exit;
|
---|
311 |
|
---|
312 | New(Result);
|
---|
313 |
|
---|
314 | // Read Only, CDROMI uses constants and ignores changes to these...
|
---|
315 | Result^.Channels := 2;
|
---|
316 | Result^.BitsPerSample:= 16;
|
---|
317 | Result^.SampleRate := 44100;
|
---|
318 |
|
---|
319 | CDRomPath:= '/dev/sr' + IntToStr(Drive);
|
---|
320 | slCDROMS := TStringList.Create;
|
---|
321 | Try
|
---|
322 | slCDROMS.Text:= GetSystemCDRoms;
|
---|
323 | if slCDROMS.IndexOf(CDRomPath) = -1 then
|
---|
324 | raise Exception.Create('mcwCDROM_Linux.CDROM_Open.GetSystemCDRoms Error : ' + CDRomPath + ' Not Found.');
|
---|
325 | finally
|
---|
326 | slCDROMS.Free;
|
---|
327 | end;
|
---|
328 |
|
---|
329 | Result^.hCDROM:= fpopen(PAnsiChar(CDRomPath), O_RDONLY or O_NONBLOCK);;
|
---|
330 | if Result^.hCDROM < 0 then
|
---|
331 | raise Exception.Create('mcwCDROM_Linux.CDROM_Open.fpopen Error : (' + IntToStr(Result^.hCDROM) + ') On ' + CDRomPath);
|
---|
332 |
|
---|
333 | // What State Is CDROM in ?
|
---|
334 | Res:= fpioctl(Result^.hCDROM, CDROM_DRIVE_STATUS, @Data);
|
---|
335 | if Res <> CDS_DISC_OK then
|
---|
336 | Begin
|
---|
337 | CDROM_Close(Result);
|
---|
338 | Dispose(Result);
|
---|
339 | Result:= nil;
|
---|
340 | Exit;
|
---|
341 | end;
|
---|
342 | // CDRom OK, What Kind Of Disk Do We Have?
|
---|
343 | Res := fpioctl(Result^.hCDROM, CDROM_DISC_STATUS, @Data);
|
---|
344 | if (Res <> CDS_AUDIO) And (Res <> CDS_MIXED) Then
|
---|
345 | Begin
|
---|
346 | CDROM_Close(Result);
|
---|
347 | Dispose(Result);
|
---|
348 | Result:= nil;
|
---|
349 | Exit;
|
---|
350 | end;
|
---|
351 |
|
---|
352 | CDTI := GetTrackInfo(Result^.hCDROM, Track);
|
---|
353 | if CDTI.TrackType = cdtData then
|
---|
354 | raise Exception.Create('mcwCDROM_Linux.CDROM_Open : Trying to rip a data track');
|
---|
355 |
|
---|
356 | Result^.StartPos.Track := Track;
|
---|
357 | Result^.StartPos.msf := GetTrackMSF(Result^.hCDROM, Result^.StartPos.Track);
|
---|
358 | Result^.EndPos.Track := Track +1;
|
---|
359 | Result^.EndPos.msf := GetTrackMSF(Result^.hCDROM, Result^.EndPos.Track);
|
---|
360 |
|
---|
361 | CDTrackCount := cdrom.ReadCDTOC(CDRomPath, CDTOC);
|
---|
362 |
|
---|
363 | if (Result^.EndPos.Track in [1..CDTrackCount]) = False then
|
---|
364 | raise Exception.Create('mcwCDROM_Linux.CDROM_Open : The end track out of range' + #13 +
|
---|
365 | IntToStr(Result^.EndPos.Track) + ' Requested, 1..' + IntToStr(CDTrackCount) + ' Allowed...');
|
---|
366 |
|
---|
367 | Result^.CurrentPosition := Result^.StartPos.msf;
|
---|
368 | Result^.Position := 0;
|
---|
369 | Result^.Size := GetSize(Result);
|
---|
370 | Result^.BufStart := 1;
|
---|
371 | Result^.BufEnd := 0;
|
---|
372 | Result^.BufSize := BUF_SIZE;
|
---|
373 | Result^.TotalSamples := Result^.Size div 4;
|
---|
374 | Result^.TotalTime := floor(Result^.TotalSamples / 44100);
|
---|
375 |
|
---|
376 | Inc(Result^.fHandleVaild);
|
---|
377 |
|
---|
378 | end;
|
---|
379 |
|
---|
380 | Function CDROM_Close(var pCDROMI: PCDROMInfo): Integer;
|
---|
381 | Begin
|
---|
382 | Result := CDROM_UNKNOWNERR;
|
---|
383 | if pCDROMI^.fHandleVaild = 1 then
|
---|
384 | fpclose(pCDROMI^.hCDROM);
|
---|
385 | if pCDROMI^.fHandleVaild > 0 then
|
---|
386 | Dec(pCDROMI^.fHandleVaild);
|
---|
387 | Result := CDROM_OK;
|
---|
388 | end;
|
---|
389 |
|
---|
390 | Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord;
|
---|
391 | var
|
---|
392 | ReqLen : LongWord;
|
---|
393 | ReqFrames : LongInt;
|
---|
394 | cdaudio : cdrom_read_audio;
|
---|
395 | Ret : LongWord;
|
---|
396 | Res : LongInt;
|
---|
397 | TmpCount : longword;
|
---|
398 | Procedure getNextChunk;
|
---|
399 | Begin
|
---|
400 | TmpCount:= 0;
|
---|
401 |
|
---|
402 | // Have We Reached End On Track ?
|
---|
403 | if MSF2Frames(pCDROMI^.CurrentPosition) >= MSF2Frames(pCDROMI^.EndPos.msf) then
|
---|
404 | Begin
|
---|
405 | pCDROMI^.BufEnd := 0;
|
---|
406 | pCDROMI^.BufStart := 0;
|
---|
407 | Exit;
|
---|
408 | End;
|
---|
409 |
|
---|
410 | // This is not first call
|
---|
411 | if pCDROMI^.BufEnd > 0 then
|
---|
412 | Begin
|
---|
413 | // Copy Leftover Data to Start of buffer...
|
---|
414 | TmpCount:= pCDROMI^.BufEnd - pCDROMI^.BufStart;
|
---|
415 | Move(pCDROMI^.Buf[pCDROMI^.BufStart], pCDROMI^.BufTmp[1], TmpCount);
|
---|
416 | Move(pCDROMI^.BufTmp[1], pCDROMI^.Buf[1], TmpCount);
|
---|
417 | End;
|
---|
418 |
|
---|
419 | ReqFrames:= 75; // BUF_SIZE = 1 Sec Worth Data = 75 Frames = In Bytes
|
---|
420 | if MSF2Frames(pCDROMI^.CurrentPosition) + ReqFrames > MSF2Frames(pCDROMI^.EndPos.msf) then
|
---|
421 | ReqFrames:= MSF2Frames(pCDROMI^.EndPos.msf) - MSF2Frames(pCDROMI^.CurrentPosition);
|
---|
422 |
|
---|
423 | // *** Rip Next Chunk ******************************************
|
---|
424 | cdaudio.nframes := ReqFrames; // BUF_SIZE = 1 Sec Worth Data = 75 Frames = In Bytes
|
---|
425 | cdaudio.addr_format := CDROM_MSF;
|
---|
426 | cdaudio.addr.msf := pCDROMI^.CurrentPosition;
|
---|
427 | cdaudio.buf := @pCDROMI^.Buf[TmpCount +1];
|
---|
428 |
|
---|
429 | Res:= fpioctl(pCDROMI^.hCDROM, CDROMREADAUDIO, @cdaudio);
|
---|
430 | if Res <> 0 then
|
---|
431 | raise Exception.Create('mcwCDROM_Linux.CDROM_GetData.getNextChunk.fpioctl(CDROMREADAUDIO) Error : ' + IntToStr(fpgeterrno));
|
---|
432 |
|
---|
433 | pCDROMI^.CurrentPosition:= Frames2MSF(MSF2Frames(pCDROMI^.CurrentPosition) + cdaudio.nframes);
|
---|
434 |
|
---|
435 | Ret := cdaudio.nframes * CD_FRAMESIZE_RAW;
|
---|
436 | // *** End Rip Next Chunk ***************************************
|
---|
437 |
|
---|
438 | pCDROMI^.BufEnd:= TmpCount + Ret + 1;
|
---|
439 | pCDROMI^.BufStart := 1;
|
---|
440 | end;
|
---|
441 |
|
---|
442 | begin
|
---|
443 | // PortAudio expects exact amount of data, anything less causes "Blips"
|
---|
444 |
|
---|
445 | // pCDROMI.BufStart = Start Byte of Current Valid, Not Sent Buffer...
|
---|
446 | // pCDROMI.BufEnd = Last Byte of Current Valid, Not sent Buffer...
|
---|
447 |
|
---|
448 | ReqLen:= DataLength;
|
---|
449 |
|
---|
450 | if pCDROMI^.fHandleVaild = 0 then
|
---|
451 | raise Exception.Create('mcwCDROM_Linux.CDROM_GetData Error : Call To GetData Without Vaild CDROM Handle.');
|
---|
452 |
|
---|
453 | // We don't read CDROM every call, only when we need new data...
|
---|
454 | if (pCDROMI^.BufStart + ReqLen) > pCDROMI^.BufEnd then
|
---|
455 | getNextChunk;
|
---|
456 |
|
---|
457 | // is the amount in buffer less than what was requested...
|
---|
458 | if DataLength > (pCDROMI^.BufEnd - pCDROMI^.BufStart + 1) then
|
---|
459 | DataLength := pCDROMI^.BufEnd - pCDROMI^.BufStart + 1;
|
---|
460 |
|
---|
461 | // Have We Finished Reading Track ?
|
---|
462 | if pCDROMI^.BufEnd = 0 then
|
---|
463 | DataLength:= 0;
|
---|
464 |
|
---|
465 | pData:= @pCDROMI^.Buf[pCDROMI^.BufStart];
|
---|
466 |
|
---|
467 | If DataLength > 0 Then
|
---|
468 | Begin
|
---|
469 | Inc(pCDROMI^.BufStart, DataLength);
|
---|
470 | Inc(pCDROMI^.Position, DataLength);
|
---|
471 | end;
|
---|
472 |
|
---|
473 | Result:= DataLength;
|
---|
474 | end;
|
---|
475 | {$ENDIF}
|
---|
476 | //////////////////////
|
---|
477 | {$IFDEF MSWINDOWS}
|
---|
478 | const
|
---|
479 | CDDA = 2;
|
---|
480 | IOCTL_CDROM_READ_TOC = $00024000;
|
---|
481 | IOCTL_CDROM_RAW_READ = $0002403E;
|
---|
482 | IOCTL_STORAGE_CHECK_VERIFY2 = $0002D0800;
|
---|
483 | MAXIMUM_NUMBER_TRACKS = 100;
|
---|
484 | CB_CDROMSECTOR = 2048;
|
---|
485 |
|
---|
486 | Type
|
---|
487 | _TRACK_DATA = record
|
---|
488 | Reserved : UCHAR;
|
---|
489 | Control_and_Adr: UCHAR;
|
---|
490 | TrackNumber : UCHAR;
|
---|
491 | Reserved1 : UCHAR;
|
---|
492 | Address : array[0..3] of UCHAR;
|
---|
493 | end;
|
---|
494 | TRACK_DATA = _TRACK_DATA;
|
---|
495 | PTRACK_DATA = ^_TRACK_DATA;
|
---|
496 | _CDROM_TOC = record
|
---|
497 | Length : WORD;
|
---|
498 | FirstTrack : UCHAR;
|
---|
499 | LastTrack : UCHAR;
|
---|
500 | TrackData : array[0..(MAXIMUM_NUMBER_TRACKS)-1] of TRACK_DATA;
|
---|
501 | end;
|
---|
502 | CDROM_TOC = _CDROM_TOC;
|
---|
503 | PCDROM_TOC = ^_CDROM_TOC;
|
---|
504 | RAW_READ_INFO = record
|
---|
505 | DiskOffset : Int64;
|
---|
506 | SectorCount : Cardinal;
|
---|
507 | TrackMode : Cardinal;
|
---|
508 | end;
|
---|
509 |
|
---|
510 | Function fpgeterrno(): LongWord;
|
---|
511 | Begin
|
---|
512 | Result:= GetLastOSError;
|
---|
513 | end;
|
---|
514 |
|
---|
515 | // ********************** Private Windows SUPPORT Functions ************************************************************
|
---|
516 | function GetTrackMSF(Table: CDROM_TOC; Track : LongInt): Tmsf;
|
---|
517 | begin
|
---|
518 | Result.min := Table.TrackData[Track -1].Address[1];
|
---|
519 | Result.sec := Table.TrackData[Track -1].Address[2];
|
---|
520 | Result.Frame:= Table.TrackData[Track -1].Address[3];
|
---|
521 | end;
|
---|
522 |
|
---|
523 | function GetPosMSF(Table: CDROM_TOC; Pos : TCDDAPosition): Tmsf;
|
---|
524 | var
|
---|
525 | msf1 : Tmsf;
|
---|
526 | frames : longint;
|
---|
527 | begin
|
---|
528 | msf1 := GetTrackMSF(Table, Pos.Track);
|
---|
529 | frames := MSF2Frames(msf1);
|
---|
530 | frames := frames + MSF2Frames(Pos.msf);
|
---|
531 | Result := Frames2MSF(frames);
|
---|
532 | end;
|
---|
533 |
|
---|
534 | function GetSize(pCDROMI: PCDROMInfo): Int64;
|
---|
535 | var
|
---|
536 | F1 : LongWord;
|
---|
537 | F2 : LongWord;
|
---|
538 | begin
|
---|
539 | F1:= (((pCDROMI^.StartPos.msf.min * 60) + pCDROMI^.StartPos.msf.sec) * 75) + pCDROMI^.StartPos.msf.Frame;
|
---|
540 | F2:= (((pCDROMI^.EndPos.msf.min * 60) + pCDROMI^.EndPos.msf.sec) * 75) + pCDROMI^.EndPos.msf.Frame;
|
---|
541 | Result := (F2 - F1) * CD_FRAMESIZE_RAW;
|
---|
542 | end;
|
---|
543 | // *********************************************************************************************************************
|
---|
544 |
|
---|
545 | Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte;
|
---|
546 | Var
|
---|
547 | driveLetter : Char;
|
---|
548 | drive : Char;
|
---|
549 | cdromcount : Byte;
|
---|
550 | found : Boolean;
|
---|
551 | Begin
|
---|
552 | found := False;
|
---|
553 | driveLetter:= FileName[1];
|
---|
554 | cdromcount := 255;
|
---|
555 | For drive := 'A' to 'Z' do
|
---|
556 | if GetDriveType(PChar(drive + ':\')) = DRIVE_CDROM then
|
---|
557 | Begin
|
---|
558 | Inc(cdromcount);
|
---|
559 | if drive = driveLetter then
|
---|
560 | Begin
|
---|
561 | found:= True;
|
---|
562 | break;
|
---|
563 | end;
|
---|
564 | end;
|
---|
565 | if not found then
|
---|
566 | raise Exception.Create('CDROM (BDROM) Drive Not Found !');
|
---|
567 | Result := cdromcount;
|
---|
568 | end;
|
---|
569 |
|
---|
570 | Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte;
|
---|
571 | Var
|
---|
572 | s : AnsiString;
|
---|
573 | Begin
|
---|
574 | // This works on Win7...
|
---|
575 | s := Copy(Filename, Pos(kCDDA_TrackWin, Filename) + 5, Length(Filename));
|
---|
576 | s := Copy(s, 1, Pos('.', s) -1);
|
---|
577 | Result:= StrToIntDef(s , 0);
|
---|
578 | end;
|
---|
579 |
|
---|
580 | Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo;
|
---|
581 | Var
|
---|
582 | Drive, Track : Byte;
|
---|
583 | Begin
|
---|
584 | Result:= CDROM_Open(CDROM_GetFileNameDrive(FileName),
|
---|
585 | CDROM_GetFileNameTrack(FileName));
|
---|
586 | end;
|
---|
587 |
|
---|
588 | Function CDROM_Open(Drive, Track : Byte): PCDROMInfo;
|
---|
589 | Var
|
---|
590 | CDRomPath : AnsiString;
|
---|
591 | Table : CDROM_TOC;
|
---|
592 | BytesRead : LongWord;
|
---|
593 | Ret : BOOL;
|
---|
594 | flags : longword;
|
---|
595 | // Index : LongInt;
|
---|
596 | Begin
|
---|
597 | Result:= nil;
|
---|
598 | if (Drive = 255) or (Track < 1) then
|
---|
599 | Exit;
|
---|
600 |
|
---|
601 | New(Result);
|
---|
602 |
|
---|
603 | // Read Only, CDROMI uses constants and ignores changes to these...
|
---|
604 | Result^.Channels := 2;
|
---|
605 | Result^.BitsPerSample:= 16;
|
---|
606 | Result^.SampleRate := 44100;
|
---|
607 |
|
---|
608 | CDRomPath:= UpperCase('\\.\CDROM') + IntToStr(Drive);
|
---|
609 | flags := longword(GENERIC_READ);
|
---|
610 |
|
---|
611 | Result^.hCDROM:= CreateFileA(PAnsiChar(CDRomPath), Flags, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 );
|
---|
612 | if (Result^.hCDROM = INVALID_HANDLE_VALUE) then
|
---|
613 | raise Exception.Create('mcwCDROM_Win.CDROM_Open.CreateFileA Error : (' + IntToStr(Result^.hCDROM) + ') On ' + CDRomPath);
|
---|
614 |
|
---|
615 | // What State Is CDROM in ?
|
---|
616 | Ret:= DeviceIoControl(Result^.hCDROM, IOCTL_STORAGE_CHECK_VERIFY2, nil, 0, nil, 0, BytesRead, nil);
|
---|
617 | if Not Ret then
|
---|
618 | Begin
|
---|
619 | CDROM_Close(Result);
|
---|
620 | Dispose(Result);
|
---|
621 | Result:= nil;
|
---|
622 | Exit;
|
---|
623 | end;
|
---|
624 |
|
---|
625 | Ret:= DeviceIoControl(Result^.hCDROM, IOCTL_CDROM_READ_TOC, nil, 0, @Table, sizeof(Table), BytesRead, nil);
|
---|
626 | if Ret = False then
|
---|
627 | raise Exception.Create('mcwCDROM_Win.CDROM_Open.DeviceIoControl(IOCTL_CDROM_READ_TOC) Error ');
|
---|
628 |
|
---|
629 | Result^.StartPos.Track := Track;
|
---|
630 | Result^.StartPos.msf := GetTrackMSF(Table, Result^.StartPos.Track);
|
---|
631 | Result^.EndPos.Track := Track +1;
|
---|
632 | Result^.EndPos.msf := GetTrackMSF(Table, Result^.EndPos.Track);
|
---|
633 | Result^.CurrentPosition := Result^.StartPos.msf;
|
---|
634 | Result^.Position := 0;
|
---|
635 | Result^.Size := GetSize(Result);
|
---|
636 | Result^.BufStart := 1;
|
---|
637 | Result^.BufEnd := 0;
|
---|
638 | Result^.BufSize := BUF_SIZE;
|
---|
639 | Result^.TotalSamples := Result^.Size div 4;
|
---|
640 | Result^.TotalTime := floor(Result^.TotalSamples / 44100);
|
---|
641 |
|
---|
642 | Inc(Result^.fHandleVaild);
|
---|
643 | end;
|
---|
644 |
|
---|
645 | Function CDROM_Close(var pCDROMI: PCDROMInfo): Integer;
|
---|
646 | Begin
|
---|
647 | Result := CDROM_UNKNOWNERR;
|
---|
648 | if pCDROMI^.fHandleVaild = 1 then
|
---|
649 | CloseHandle(pCDROMI^.hCDROM);
|
---|
650 | if pCDROMI^.fHandleVaild > 0 then
|
---|
651 | Dec(pCDROMI^.fHandleVaild);
|
---|
652 | Result := CDROM_OK;
|
---|
653 | end;
|
---|
654 |
|
---|
655 | Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord;
|
---|
656 | var
|
---|
657 | ReqLen : LongWord;
|
---|
658 | ReqFrames : LongInt;
|
---|
659 | Info : RAW_READ_INFO;
|
---|
660 | BytesRead : LongWord;
|
---|
661 | Address : Int64;
|
---|
662 | Ret : LongWord;
|
---|
663 | Res : BOOL;
|
---|
664 | TmpCount : longword;
|
---|
665 | Procedure getNextChunk;
|
---|
666 | Begin
|
---|
667 | TmpCount:= 0;
|
---|
668 |
|
---|
669 | // Have We Reached End On Track ?
|
---|
670 | if MSF2Frames(pCDROMI^.CurrentPosition) >= MSF2Frames(pCDROMI^.EndPos.msf) then
|
---|
671 | Begin
|
---|
672 | pCDROMI^.BufEnd := 0;
|
---|
673 | pCDROMI^.BufStart := 0;
|
---|
674 | Exit;
|
---|
675 | End;
|
---|
676 |
|
---|
677 | // This is not first call
|
---|
678 | if pCDROMI^.BufEnd > 0 then
|
---|
679 | Begin
|
---|
680 | // Copy Leftover Data to Start of buffer...
|
---|
681 | TmpCount:= pCDROMI^.BufEnd - pCDROMI^.BufStart;
|
---|
682 | Move(pCDROMI^.Buf[pCDROMI^.BufStart], pCDROMI^.BufTmp[1], TmpCount);
|
---|
683 | Move(pCDROMI^.BufTmp[1], pCDROMI^.Buf[1], TmpCount);
|
---|
684 | End;
|
---|
685 | // While Linux Can deal With 75 Frame Request, Windows Only 20 (?)
|
---|
686 | ReqFrames:= 20; // BUF_SIZE = 1 Sec Worth Data = 75 Frames = In Bytes
|
---|
687 | if MSF2Frames(pCDROMI^.CurrentPosition) + ReqFrames > MSF2Frames(pCDROMI^.EndPos.msf) then
|
---|
688 | ReqFrames:= MSF2Frames(pCDROMI^.EndPos.msf) - MSF2Frames(pCDROMI^.CurrentPosition);
|
---|
689 |
|
---|
690 | // *** Rip Next Chunk ******************************************
|
---|
691 | Address:= AddressToSectors(pCDROMI^.CurrentPosition);
|
---|
692 |
|
---|
693 | Info.TrackMode := CDDA;
|
---|
694 | Info.SectorCount:= ReqFrames;
|
---|
695 | Info.DiskOffset := Address * CB_CDROMSECTOR;
|
---|
696 |
|
---|
697 | Res:= DeviceIoControl(pCDROMI^.hCDROM,
|
---|
698 | IOCTL_CDROM_RAW_READ,
|
---|
699 | @Info,
|
---|
700 | sizeof(Info),
|
---|
701 | @pCDROMI^.Buf[TmpCount +1],
|
---|
702 | ReqFrames * CD_FRAMESIZE_RAW,
|
---|
703 | BytesRead,
|
---|
704 | nil);
|
---|
705 | if Res = False then
|
---|
706 | raise Exception.Create('mcwCDROM_Win.CDROM_GetData.getNextChunk.fpioctl(CDROMREADAUDIO) Error : ' + IntToStr(fpgeterrno));
|
---|
707 |
|
---|
708 | pCDROMI^.CurrentPosition:= Frames2MSF(MSF2Frames(pCDROMI^.CurrentPosition) + ReqFrames);
|
---|
709 |
|
---|
710 | Ret := BytesRead; // Should Be The same as "ReqFrames * CD_FRAMESIZE_RAW"
|
---|
711 | // *** End Rip Next Chunk ***************************************
|
---|
712 |
|
---|
713 | pCDROMI^.BufEnd:= TmpCount + Ret + 1;
|
---|
714 | pCDROMI^.BufStart := 1;
|
---|
715 | end;
|
---|
716 |
|
---|
717 | begin
|
---|
718 | // PortAudio expects exact amount of data, anything less causes "Blips"
|
---|
719 |
|
---|
720 | // pCDROMI.BufStart = Start Byte of Current Valid, Not Sent Buffer...
|
---|
721 | // pCDROMI.BufEnd = Last Byte of Current Valid, Not sent Buffer...
|
---|
722 |
|
---|
723 | ReqLen:= DataLength;
|
---|
724 |
|
---|
725 | if pCDROMI^.fHandleVaild = 0 then
|
---|
726 | raise Exception.Create('mcwCDROM_Win.CDROM_GetData Error : Call To GetData Without Vaild CDROM Handle.');
|
---|
727 |
|
---|
728 | // We don't read CDROM every call, only when we need new data...
|
---|
729 | if (pCDROMI^.BufStart + ReqLen) > pCDROMI^.BufEnd then
|
---|
730 | getNextChunk;
|
---|
731 |
|
---|
732 | // is the amount in buffer less than what was requested...
|
---|
733 | if DataLength > (pCDROMI^.BufEnd - pCDROMI^.BufStart + 1) then
|
---|
734 | DataLength := pCDROMI^.BufEnd - pCDROMI^.BufStart + 1;
|
---|
735 |
|
---|
736 | // Have We Finished Reading Track ?
|
---|
737 | if pCDROMI^.BufEnd = 0 then
|
---|
738 | DataLength:= 0;
|
---|
739 |
|
---|
740 | pData:= @pCDROMI^.Buf[pCDROMI^.BufStart];
|
---|
741 |
|
---|
742 | If DataLength > 0 Then
|
---|
743 | Begin
|
---|
744 | Inc(pCDROMI^.BufStart, DataLength);
|
---|
745 | Inc(pCDROMI^.Position, DataLength);
|
---|
746 | end;
|
---|
747 |
|
---|
748 | Result:= DataLength;
|
---|
749 | end;
|
---|
750 | {$ENDIF}
|
---|
751 | /////////////////////
|
---|
752 |
|
---|
753 | Function GetSystemCDRoms: AnsiString;
|
---|
754 | var
|
---|
755 | Index : longint;
|
---|
756 | Ret : LongInt;
|
---|
757 | Devices : Array of string;
|
---|
758 | sl : TStringList;
|
---|
759 | begin
|
---|
760 | Result:= '';
|
---|
761 | sl:= TStringList.Create;
|
---|
762 |
|
---|
763 | SetLength(Devices, 99);
|
---|
764 | Ret:= cdrom.GetCDRomDevices(Devices);
|
---|
765 | If Ret > 0 Then
|
---|
766 | For Index := 0 To Ret -1 do
|
---|
767 | sl.Add(Devices[Index]);
|
---|
768 |
|
---|
769 | Result:= sl.Text;
|
---|
770 | sl.Free;
|
---|
771 | end;
|
---|
772 |
|
---|
773 | // ********************** Common Linux/Windows SUPPORT Functions *******************************************************
|
---|
774 | Function Frames2MSF(Frames : LongInt) : Tmsf;
|
---|
775 | var
|
---|
776 | Temp : Integer;
|
---|
777 | begin
|
---|
778 | Temp := Frames div 75;
|
---|
779 | Result.min := Temp div 60;
|
---|
780 | Result.sec := Temp mod 60;
|
---|
781 | Result.Frame := Frames mod 75;
|
---|
782 | end;
|
---|
783 |
|
---|
784 | function MSF2Frames(const msf : Tmsf) : LongInt;
|
---|
785 | begin
|
---|
786 | Result := ((msf.min * 60) + msf.sec) * 75 + msf.Frame;
|
---|
787 | end;
|
---|
788 |
|
---|
789 | Function AddressToSectors (msf : Tmsf): int64;
|
---|
790 | begin
|
---|
791 | Result:= MSF2Frames(msf) - 150;
|
---|
792 | end;
|
---|
793 |
|
---|
794 | // *********************************************************************************************************************
|
---|
795 |
|
---|
796 | end.
|
---|
797 |
|
---|