source: trunk/Packages/uos/uos_cdrom.pas

Last change on this file was 665, checked in by chronos, 45 hours ago
  • Fixed: Playing music on Windows.
File size: 23.6 KB
Line 
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
8unit uos_cdrom;
9
10{$mode objfpc}{$H+}
11
12interface
13
14uses
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
35Type
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
102Const
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
123implementation
124
125Function LoadCDROM(Lib : AnsiString): LongWord;
126Begin
127 Result:= 32767;
128 end;
129
130Function UnloadCDROM: LongWord;
131Begin
132 Result:= 32767;
133 end;
134
135Function isCDROMLoaded : Boolean;
136Begin
137 Result:= True;
138 end;
139
140{$IFDEF unix}
141Const
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
161type
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 **************************************************************
186function GetTrackInfo(hCDROM: LongInt; Track : LongInt): TCDDATrackInfo;
187var
188 Entry : cdrom_tocentry;
189 TOC : cdrom_tochdr;
190 F1 : LongInt;
191 F2 : LongInt;
192 Ret : LongInt;
193begin
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);
223end;
224
225function GetTrackMSF(hCDROM, Track : LongInt): Tmsf;
226var
227 entry : cdrom_tocentry;
228 hdr : cdrom_tochdr;
229 Ret : LongInt;
230begin
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;
245end;
246
247function GetPosMSF(hCDROM: LongInt; Pos : TCDDAPosition): Tmsf;
248var
249 msf1 : Tmsf;
250 frames : longint;
251begin
252 msf1 := GetTrackMSF(hCDROM, Pos.Track);
253 frames := MSF2Frames(msf1);
254 frames := frames + MSF2Frames(Pos.msf);
255 Result := Frames2MSF(frames);
256end;
257
258function GetSize(pCDROMI: PCDROMInfo): Int64;
259var
260 F1 : LongWord;
261 F2 : LongWord;
262begin
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;
266end;
267// *********************************************************************************************************************
268
269Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte;
270Begin
271 Result:= 255; // Assume Error
272 if Copy(FileName, 1, Length(kCDDA_Base)) = kCDDA_Base then
273 Result:= StrToIntDef(FileName[10], 256);
274end;
275
276Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte;
277Var
278 s : AnsiString;
279Begin
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;
287end;
288
289Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo;
290Var
291 Drive, Track : Byte;
292Begin
293 Result:= CDROM_Open(CDROM_GetFileNameDrive(FileName),
294 CDROM_GetFileNameTrack(FileName));
295end;
296
297Function CDROM_Open(Drive, Track : Byte): PCDROMInfo;
298Var
299 CDRomPath : AnsiString;
300 slCDROMS : TStringList;
301
302 Res : longint;
303 Data : longint;
304 CDTI : TCDDATrackInfo;
305 CDTrackCount : LongInt;
306 CDTOC : Array of TTocEntry;
307Begin
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
378end;
379
380Function CDROM_Close(var pCDROMI: PCDROMInfo): Integer;
381Begin
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;
388end;
389
390Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord;
391var
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
442begin
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;
474end;
475{$ENDIF}
476//////////////////////
477{$IFDEF MSWINDOWS}
478const
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
486Type
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
510Function fpgeterrno(): LongWord;
511Begin
512 Result:= GetLastOSError;
513end;
514
515// ********************** Private Windows SUPPORT Functions ************************************************************
516function GetTrackMSF(Table: CDROM_TOC; Track : LongInt): Tmsf;
517begin
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];
521end;
522
523function GetPosMSF(Table: CDROM_TOC; Pos : TCDDAPosition): Tmsf;
524var
525 msf1 : Tmsf;
526 frames : longint;
527begin
528 msf1 := GetTrackMSF(Table, Pos.Track);
529 frames := MSF2Frames(msf1);
530 frames := frames + MSF2Frames(Pos.msf);
531 Result := Frames2MSF(frames);
532end;
533
534function GetSize(pCDROMI: PCDROMInfo): Int64;
535var
536 F1 : LongWord;
537 F2 : LongWord;
538begin
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;
542end;
543// *********************************************************************************************************************
544
545Function CDROM_GetFileNameDrive(FileName : AnsiString): Byte;
546Var
547 driveLetter : Char;
548 drive : Char;
549 cdromcount : Byte;
550 found : Boolean;
551Begin
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;
568end;
569
570Function CDROM_GetFileNameTrack(FileName : AnsiString): Byte;
571Var
572 s : AnsiString;
573Begin
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);
578end;
579
580Function CDROM_OpenFile(FileName : AnsiString): PCDROMInfo;
581Var
582 Drive, Track : Byte;
583Begin
584 Result:= CDROM_Open(CDROM_GetFileNameDrive(FileName),
585 CDROM_GetFileNameTrack(FileName));
586end;
587
588Function CDROM_Open(Drive, Track : Byte): PCDROMInfo;
589Var
590 CDRomPath : AnsiString;
591 Table : CDROM_TOC;
592 BytesRead : LongWord;
593 Ret : BOOL;
594 flags : longword;
595// Index : LongInt;
596Begin
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);
643end;
644
645Function CDROM_Close(var pCDROMI: PCDROMInfo): Integer;
646Begin
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;
653end;
654
655Function CDROM_GetData(var pCDROMI: PCDROMInfo; var pData: Pointer; var DataLength: longword): LongWord;
656var
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
717begin
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;
749end;
750{$ENDIF}
751/////////////////////
752
753Function GetSystemCDRoms: AnsiString;
754var
755 Index : longint;
756 Ret : LongInt;
757 Devices : Array of string;
758 sl : TStringList;
759begin
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;
771end;
772
773// ********************** Common Linux/Windows SUPPORT Functions *******************************************************
774Function Frames2MSF(Frames : LongInt) : Tmsf;
775var
776 Temp : Integer;
777begin
778 Temp := Frames div 75;
779 Result.min := Temp div 60;
780 Result.sec := Temp mod 60;
781 Result.Frame := Frames mod 75;
782end;
783
784function MSF2Frames(const msf : Tmsf) : LongInt;
785begin
786 Result := ((msf.min * 60) + msf.sec) * 75 + msf.Frame;
787end;
788
789Function AddressToSectors (msf : Tmsf): int64;
790begin
791 Result:= MSF2Frames(msf) - 150;
792end;
793
794// *********************************************************************************************************************
795
796end.
797
Note: See TracBrowser for help on using the repository browser.