CDRecord.Mod 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421
  1. MODULE CDRecord;
  2. (*
  3. References:
  4. Mt. Fuji Commands for Multimedia Devices (SFF8090i v6)
  5. ECMA-130 Data interchange on read-only 120 mm optical data disks
  6. *)
  7. IMPORT SYSTEM, Kernel, ATADisks, Lib := CDRecordLib, Utils := CDRecordUtils, Disks, Plugins, KernelLog, Files, Strings, Objects, MakeIsoImages;
  8. CONST
  9. ResOk=0; ResErr=1;
  10. Debug = FALSE;
  11. UseDma = TRUE;
  12. UseBufferedReader = TRUE;
  13. MaxRecorders* = 2;
  14. BufferSize = 16*1024*1024;
  15. RawSectorSize = 2352;
  16. BlockSize = 2048;
  17. DataSectorSize = 2048; (* Yellow Book Mode 1 *)
  18. AudioSectorSize = RawSectorSize;
  19. TrackLimit = 99;
  20. TransferSize = 200; (* in sectors *)
  21. MinTrackSize = 300; (* in sectors *)
  22. DefaultPregap = 150; (* in sectors *)
  23. InitialPregap = 150; (* initial pregap which is not accessible with logical addressing *)
  24. (* incremental writing *)
  25. NoRunInBlocks = 2;
  26. NoRunOutBlocks = 1;
  27. NoLinkBlocks = 4;
  28. FifoSize = 16*1024*1024;
  29. ListCurrentMaxSpeeds = FALSE; (* not recommended *)
  30. NotificationPeriod = 1000; (* client is informed every NotificationPeriod about status *)
  31. SingleSpeed* = 176; (* kByte/s *)
  32. (* Burn Settings *)
  33. TrackAtOnce* = Lib.WTTao; SessionAtOnce* = Lib.WTSao;
  34. tnoLeadout* = 0AAH; (* track number of lead out*)
  35. (* Track Types *)
  36. AudioTrack* = 0; DataTrack* = 1;
  37. BestSpeed = 0FFFFH;
  38. (* track properties *)
  39. (* media functions *)
  40. MFCdRw* = 0; MFCdr* = 1; MFSao* = 2; MFBufe* = 3; MFMultisession* = 4; MFCaddy* = 5; MFTray* = 6; MFPopup* = 7;
  41. (* Errors *)
  42. ErrFileNotFound* = 3000;
  43. ErrTrackSizeExceeded* = 3001;
  44. ErrNotEnoughFreeSpace* = 3003;
  45. ErrNoMediumPresent* = 3004;
  46. ErrDriveNotReady* = 3005;
  47. ErrNoIsoFile* = 3006;
  48. ErrWrongWaveFile* = 3007; (* not an appropriate wav file (must be 16 bit encoded, 44.1kHz and 2 channel) *)
  49. ErrCalibrationFailed* = 3008;
  50. ErrDiscNotEmpty* = 3009;
  51. ErrCDRWNotEmpty* = 3010;
  52. ErrDiscNotAppendable*= 3011;
  53. ErrCDRWNotAppendable* = 3012;
  54. ErrSendingCueSheet* = 3013;
  55. ErrIncompatibleMedium* = 3014;
  56. ErrWriting* = 3015;
  57. ErrVerificationFailed* = 3016;
  58. (* record operations *)
  59. Writing* = 0; ClosingTrack* = 1; ClosingSession* = 2; SendingCueSheet* = 3; Calibrating* = 4; FillingFifo* = 5; FlushingCache* = 6; Verifying* = 7;
  60. TYPE
  61. Buffer = POINTER TO ARRAY OF CHAR;
  62. Capabilities* = RECORD
  63. writeSpeeds* : POINTER TO ARRAY OF LONGINT;
  64. mediaFunc* : SET;
  65. END;
  66. WriteParams = RECORD
  67. writeType, multisession, trackMode, DBType: LONGINT;
  68. testWrite, bufe: BOOLEAN;
  69. END;
  70. RecordingStatus* = OBJECT(Utils.Status)
  71. VAR
  72. currentSpeed*: LONGINT; (* raw speed *)
  73. freeBuffer*, bufferSize*, operation*, secsTransferred*, secsVerified*: LONGINT;
  74. empty*: LONGINT; (* number of times sw buffer was empty *)
  75. END RecordingStatus;
  76. BurnSettings* = RECORD
  77. writeType*: LONGINT;
  78. verify*, bufe*, multisession*, append*: BOOLEAN;
  79. speed*: LONGINT;
  80. END;
  81. Disc* = OBJECT
  82. VAR
  83. erasable*: BOOLEAN;
  84. status*, statusLastSession*, nofSessions*: LONGINT;
  85. usedBlocks*, freeBlocks*: LONGINT;
  86. type*: LONGINT;
  87. latestLeadOut*: MSF;
  88. END Disc;
  89. (* extended disc information for CDR/CDRW media*)
  90. DiscEx* = OBJECT(Disc)
  91. VAR
  92. refSpeed*: LONGINT; (* valid only for CDRW *)
  93. minSpeed*, maxSpeed*: LONGINT;
  94. subtype*: LONGINT;
  95. END DiscEx;
  96. Track = OBJECT
  97. VAR
  98. tno, padding, nofsecs, startSec, secsize, type: LONGINT;
  99. bytespt: LONGINT; (* bytes per transfer *)
  100. secspt: LONGINT; (* secs per transfer *)
  101. pregap: LONGINT; (* pregap in sectors *)
  102. size: LONGINT; (* size of track in bytes *)
  103. permission: BOOLEAN;
  104. PROCEDURE &New*(tno, trackType: LONGINT; permitCopy: BOOLEAN);
  105. BEGIN
  106. SELF.tno := tno;
  107. SELF.permission := permitCopy;
  108. SELF.type := trackType;
  109. IF type = AudioTrack THEN
  110. secsize := AudioSectorSize;
  111. ELSE
  112. secsize := DataSectorSize;
  113. END;
  114. secspt := TransferSize;
  115. bytespt := secsize*secspt;
  116. END New;
  117. END Track;
  118. InformationTrack* = OBJECT(Track)
  119. VAR
  120. file-: Files.File;
  121. PROCEDURE &NewInfTrack*(tno, trackType: LONGINT; permitCopy: BOOLEAN; file: Files.File);
  122. BEGIN
  123. New(tno, trackType, permitCopy);
  124. SELF.file := file;
  125. pregap := DefaultPregap;
  126. InitTrack();
  127. END NewInfTrack;
  128. PROCEDURE InitTrack;
  129. BEGIN
  130. padding := 0;
  131. nofsecs := file.Length() DIV secsize;
  132. IF file.Length() MOD secsize # 0 THEN
  133. INC(nofsecs);
  134. padding := secsize - (file.Length() MOD secsize);
  135. END;
  136. IF nofsecs < MinTrackSize THEN
  137. INC(padding, (MinTrackSize - nofsecs) * secsize);
  138. nofsecs := MinTrackSize;
  139. END;
  140. size := nofsecs*secsize;
  141. END InitTrack;
  142. END InformationTrack;
  143. MSF = RECORD
  144. min, sec, frame: LONGINT;
  145. END;
  146. CueSheetEntry = RECORD
  147. ctladr, tNo, index, dataForm: CHAR;
  148. scms, min, sec, frame: CHAR;
  149. END;
  150. CueSheet = OBJECT
  151. VAR
  152. nofEntries, cur: LONGINT;
  153. adr: ADDRESS;
  154. buf: POINTER TO ARRAY OF CueSheetEntry;
  155. PROCEDURE &New*(compilation: Compilation);
  156. BEGIN
  157. nofEntries := 2*compilation.nofTracks - 2;
  158. NEW(buf, nofEntries);
  159. adr := ADDRESSOF(buf[0]);
  160. GenerateSheet(compilation.tracks, compilation.nofTracks);
  161. END New;
  162. PROCEDURE GenerateSheet(CONST tracks: ARRAY OF Track; nofTracks: LONGINT);
  163. VAR
  164. i: LONGINT;
  165. df: CHAR;
  166. BEGIN
  167. FOR i := 0 TO nofTracks - 1 DO
  168. IF i = 0 THEN (* leadin *)
  169. Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMLeadin);
  170. AddEntry(tracks[i], 0, df);
  171. ELSIF i = nofTracks-1 THEN (* leadout *)
  172. Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMLeadout);
  173. AddEntry(tracks[i], 1, df);
  174. ELSE
  175. IF tracks[i].type = AudioTrack THEN
  176. Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMDigitalAudio);
  177. ELSE
  178. Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMCdRomMode1);
  179. END;
  180. (* we always add index 0 even if there is no pregap*)
  181. AddEntry(tracks[i], 0, df);
  182. AddEntry(tracks[i], 1, df);
  183. END;
  184. END;
  185. END GenerateSheet;
  186. PROCEDURE AddEntry(track: Track; index: LONGINT; df: CHAR);
  187. VAR
  188. ctladr: CHAR;
  189. startSec: LONGINT;
  190. msf: MSF;
  191. entry: CueSheetEntry;
  192. nibble: SET; (* q channel nibble *)
  193. BEGIN
  194. startSec := track.startSec;
  195. IF index = 0 THEN
  196. DEC(startSec, track.pregap);
  197. END;
  198. nibble := {};
  199. IF track.type = DataTrack THEN INCL(nibble, Lib.QCDataTrack) END;
  200. IF track.permission THEN INCL(nibble, Lib.QCCopyPermitted) END;
  201. Lib.SetField(ctladr, Lib.CTLMask, Lib.CTLOfs, SYSTEM.VAL(LONGINT, nibble));
  202. Lib.SetField(ctladr, Lib.ADRMask, Lib.ADROfs, Lib.ADRTno);
  203. SectorToMsf(startSec, msf);
  204. entry.ctladr := ctladr;
  205. entry.tNo := CHR(track.tno);
  206. entry.index := CHR(index);
  207. entry.dataForm := df;
  208. entry.scms := 0X;
  209. entry.min := CHR(msf.min);
  210. entry.sec := CHR(msf.sec);
  211. entry.frame :=CHR(msf.frame);
  212. buf[cur] := entry;
  213. INC(cur);
  214. END AddEntry;
  215. PROCEDURE Print;
  216. VAR
  217. i: LONGINT;
  218. entry: CueSheetEntry;
  219. BEGIN
  220. KernelLog.Ln;
  221. FOR i := 0 TO nofEntries - 1 DO
  222. entry := buf[i];
  223. KernelLog.Int(ORD(entry.ctladr), 5);
  224. KernelLog.Int(ORD(entry.tNo), 5);
  225. KernelLog.Int(ORD(entry.index), 5);
  226. KernelLog.Int(ORD(entry.dataForm), 5);
  227. KernelLog.Int(ORD(entry.scms), 5);
  228. KernelLog.Int(ORD(entry.min), 5);
  229. KernelLog.Int(ORD(entry.sec), 5);
  230. KernelLog.Int(ORD(entry.frame), 5);
  231. KernelLog.Ln;
  232. END;
  233. END Print;
  234. END CueSheet;
  235. Compilation* = OBJECT
  236. VAR
  237. nofTracks-, totalSize-: LONGINT;
  238. tracks-: ARRAY TrackLimit OF Track;
  239. PROCEDURE &New*;
  240. BEGIN
  241. nofTracks := 0;
  242. totalSize := -InitialPregap; (* 150 sector pregap of first track is not accessible with logical addressing *)
  243. END New;
  244. PROCEDURE Finish*;
  245. VAR
  246. i: LONGINT;
  247. BEGIN
  248. ASSERT(nofTracks >= 1);
  249. (* leadin *)
  250. NEW(tracks[0], 0H, tracks[1].type, tracks[1].permission);
  251. tracks[0].pregap := InitialPregap;
  252. INC(nofTracks, 1);
  253. (* leadout *)
  254. NEW(tracks[nofTracks], tnoLeadout, tracks[nofTracks-1].type, tracks[nofTracks-1].permission);
  255. INC(nofTracks, 1);
  256. (* set the start sector for each track *)
  257. FOR i := 1 TO nofTracks-1 DO
  258. INC(totalSize, tracks[i].pregap);
  259. tracks[i].startSec := totalSize;
  260. INC(totalSize, tracks[i].nofsecs);
  261. END;
  262. END Finish;
  263. (* Returns the number of total sectors *)
  264. PROCEDURE GetSize*(raw, secs: BOOLEAN): LONGINT;
  265. VAR
  266. i, size: LONGINT;
  267. BEGIN
  268. size := 0;
  269. IF raw THEN (* incl lead in ... *)
  270. FOR i := 1 TO nofTracks - 1 DO
  271. IF secs THEN
  272. INC(size, tracks[i].nofsecs + tracks[i].pregap);
  273. ELSE
  274. INC(size, tracks[i].nofsecs * tracks[i].secsize + tracks[i].pregap*tracks[i].secsize);
  275. END;
  276. END;
  277. ELSE
  278. FOR i := 1 TO nofTracks -2 DO
  279. IF secs THEN
  280. INC(size, tracks[i].nofsecs);
  281. ELSE
  282. INC(size, tracks[i].nofsecs * tracks[i].secsize);
  283. END;
  284. END;
  285. END;
  286. RETURN size;
  287. END GetSize;
  288. PROCEDURE AddTrack*(filename: Strings.String; trackType : LONGINT; permitCopy: BOOLEAN) : LONGINT;
  289. VAR
  290. res: WORD;
  291. track: InformationTrack;
  292. file: Files.File;
  293. BEGIN
  294. IF nofTracks >= TrackLimit THEN
  295. RETURN ErrTrackSizeExceeded;
  296. ELSIF FileExists(filename) THEN
  297. IF trackType = AudioTrack THEN
  298. IF ~IsWavFile(filename) THEN
  299. RETURN ErrWrongWaveFile;
  300. END;
  301. ELSE
  302. IF ~IsIsoFile(filename) THEN
  303. RETURN ErrNoIsoFile;
  304. END;
  305. END;
  306. INC(nofTracks, 1);
  307. file := Files.Old(filename^);
  308. NEW(track, nofTracks, trackType, permitCopy, file);
  309. tracks[nofTracks] := track;
  310. res := ResOk;
  311. ELSE
  312. res := ErrFileNotFound;
  313. END;
  314. RETURN ResOk;
  315. END AddTrack;
  316. END Compilation;
  317. CDRecorder* = OBJECT
  318. VAR
  319. cap*: Capabilities;
  320. dev*: ATADisks.DeviceATAPI;
  321. recStatus*: RecordingStatus;
  322. onRecordStatusChanged: Utils.StatusProc;
  323. name*: ARRAY 128 OF CHAR;
  324. reader: Reader;
  325. locked*: BOOLEAN;
  326. dma*: BOOLEAN;
  327. timer: Kernel.Timer;
  328. PROCEDURE &New*(VAR dev: ATADisks.DeviceATAPI; cap: Capabilities);
  329. BEGIN
  330. SELF.dev := dev;
  331. dma := ATADisks.DMABit IN SYSTEM.VAL(SET, dev.id.type);
  332. SELF.cap := cap;
  333. COPY(dev.desc, name);
  334. onRecordStatusChanged := NIL;
  335. NEW(timer);
  336. END New;
  337. PROCEDURE UpdateCapacity*;
  338. VAR
  339. size: LONGINT; res: WORD;
  340. BEGIN
  341. dev.GetSize(size, res);
  342. END UpdateCapacity;
  343. PROCEDURE GetBufferCapacity(VAR totalCapacity, unusedCapacity: LONGINT): WORD;
  344. VAR
  345. capacity: Lib.BufferCapacity;
  346. res: WORD;
  347. BEGIN
  348. res := Lib.ReadBufferCapacity(dev, FALSE, ADDRESSOF(capacity), SIZEOF(Lib.BufferCapacity));
  349. IF res = ResOk THEN
  350. totalCapacity := Utils.ConvertBE32Int(capacity.BufferLength);
  351. unusedCapacity := Utils.ConvertBE32Int(capacity.BlankLength);
  352. END;
  353. RETURN res;
  354. END GetBufferCapacity;
  355. PROCEDURE Record*(VAR compilation: Compilation; settings: BurnSettings; onRecordStatusChanged: Utils.StatusProc): WORD;
  356. VAR
  357. disc: Disc;
  358. res: WORD; i, op, nwa, secs: LONGINT;
  359. nibble: SET; (* the control nibble of the q channel *)
  360. params: WriteParams;
  361. cuesheet: CueSheet;
  362. track: InformationTrack;
  363. uReader: UnbufferedReader;
  364. bufReader: BufferedReader;
  365. BEGIN {EXCLUSIVE}
  366. SELF.onRecordStatusChanged := onRecordStatusChanged;
  367. NEW(recStatus);
  368. NEW(disc);
  369. res := GetDiscInfo(disc);
  370. IF res # ResOk THEN RETURN res END;
  371. IF settings.append THEN
  372. IF (disc.status # Lib.DSEmpty) & (disc.status # Lib.DSAppendable) THEN
  373. IF disc.erasable THEN
  374. RETURN ErrCDRWNotAppendable;
  375. ELSE
  376. RETURN ErrDiscNotAppendable;
  377. END;
  378. END;
  379. ELSE
  380. IF (disc.status # Lib.DSEmpty) THEN
  381. IF disc.erasable THEN
  382. RETURN ErrCDRWNotEmpty;
  383. ELSE
  384. RETURN ErrDiscNotEmpty;
  385. END;
  386. END;
  387. END;
  388. IF compilation.GetSize(TRUE, TRUE) > disc.freeBlocks THEN RETURN ErrNotEnoughFreeSpace END;
  389. Lock();
  390. IF SetWriteSpeed(settings.speed) # ResOk THEN Abort(); RETURN ResErr END;
  391. recStatus.operation := Calibrating; StatusChanged();
  392. IF Lib.SendOPCInformation(dev, TRUE) # ResOk THEN Abort(); RETURN ErrCalibrationFailed END;
  393. (* IF settings.verify THEN op := ATADisks.WriteAndVerify ELSE op := Disks.Write END; *)
  394. op := Disks.Write; (* we only verify after having written the whole compilation *)
  395. InitWriteParams(settings, params);
  396. IF UseBufferedReader THEN
  397. NEW(bufReader, compilation, TransferSize*RawSectorSize);
  398. recStatus.operation := FillingFifo; StatusChanged();
  399. bufReader.Init();
  400. reader := bufReader;
  401. ELSE
  402. NEW(uReader, compilation, TransferSize*RawSectorSize);
  403. reader := uReader;
  404. END;
  405. IF settings.writeType = SessionAtOnce THEN
  406. IF SetWriteParams(params, FALSE) # ResOk THEN Abort(); RETURN ResErr END;
  407. IF Debug THEN res := PrintWriteParams() END;
  408. NEW(cuesheet, compilation);
  409. IF Debug THEN cuesheet.Print() END;
  410. recStatus.operation := SendingCueSheet; StatusChanged();
  411. IF Lib.SendCueSheet(dev, cuesheet.adr, cuesheet.nofEntries*SIZEOF(CueSheetEntry)) # ResOk THEN
  412. res := dev.RequestSense(); Abort(); RETURN ErrSendingCueSheet;
  413. END;
  414. (* in case of multisession. but most recorder don't support multisession in sao mode anyway *)
  415. IF Lib.GetNextAddress(dev, nwa) # ResOk THEN RETURN ResErr END;
  416. (* some drive return wrong start sec for sao if disc is empty *)
  417. IF nwa <= 0 THEN nwa := -InitialPregap END;
  418. IF settings.multisession THEN
  419. FOR i := 0 TO compilation.nofTracks - 1 DO
  420. INC(compilation.tracks[i].startSec, nwa + InitialPregap);
  421. END;
  422. END;
  423. END;
  424. (* Read Capacity returns a capacity of 1 for empty media so we have to set it explicitly*)
  425. MsfToSector(disc.latestLeadOut, secs);
  426. dev.SetCapacity(secs);
  427. EXCL(dev.flags, Disks.ReadOnly);
  428. FOR i := 1 TO compilation.nofTracks-2 DO
  429. track := compilation.tracks[i](InformationTrack);
  430. (* set blockSize *)
  431. dev.SetBlockSize(track.secsize);
  432. IF settings.writeType = TrackAtOnce THEN
  433. nibble := {};
  434. IF track.type = DataTrack THEN
  435. INCL(nibble, Lib.QCDataTrack);
  436. params.DBType := Lib.DBIsoMode1;
  437. ELSE
  438. params.DBType := Lib.DBRaw;
  439. END;
  440. IF track.permission THEN INCL(nibble, Lib.QCCopyPermitted) END;
  441. params.trackMode := SYSTEM.VAL(LONGINT, nibble);
  442. IF (SetWriteParams(params, FALSE) # ResOk) OR (Lib.GetNextAddress(dev, track.startSec) # ResOk) THEN Abort(); RETURN ResErr END;
  443. IF Debug THEN res := PrintWriteParams() END;
  444. ELSE
  445. res := PadTrack(op, track);
  446. IF res # ResOk THEN Abort(); RETURN ErrWriting END;
  447. END;
  448. res := WriteTrack(op, track);
  449. IF res # ResOk THEN Abort(); RETURN ErrWriting END;
  450. IF settings.writeType = TrackAtOnce THEN
  451. (* close Track: Write PMA *)
  452. recStatus.operation := ClosingTrack; StatusChanged();
  453. res := Lib.CloseTrackSess(dev, TRUE, Lib.CFTrack, Lib.TRInvisible);
  454. WaitUntilFinished();
  455. END;
  456. END;
  457. IF settings.writeType = TrackAtOnce THEN
  458. (* close Session: write PMA to toc *)
  459. recStatus.operation := ClosingSession; StatusChanged();
  460. res := Lib.CloseTrackSess(dev, TRUE, Lib.CFSession, Lib.Ignore);
  461. ELSE
  462. recStatus.operation := FlushingCache; StatusChanged();
  463. res := Lib.SynchronizeCache(dev, TRUE);
  464. END;
  465. WaitUntilFinished();
  466. IF settings.verify THEN
  467. recStatus.operation := Verifying; StatusChanged();
  468. IF VerifyCompilation(compilation) # ResOk THEN
  469. IF Debug THEN GetSense(dev) END;
  470. Abort(); RETURN ErrVerificationFailed
  471. END;
  472. END;
  473. Unlock();
  474. INCL(dev.flags, Disks.ReadOnly);
  475. IF (reader # NIL) & (reader IS BufferedReader) THEN
  476. recStatus.empty := reader(BufferedReader).empty;
  477. ELSE
  478. recStatus.empty := -1;
  479. END;
  480. reader := NIL;
  481. RETURN ResOk;
  482. END Record;
  483. PROCEDURE InitWriteParams(VAR settings: BurnSettings; VAR params: WriteParams);
  484. BEGIN
  485. params.writeType := settings.writeType;
  486. params.bufe := settings.bufe;
  487. IF settings.multisession THEN
  488. params.multisession := Lib.MSNextSessB0;
  489. ELSE
  490. params.multisession := Lib.MSNoNextSessNoB0;
  491. END;
  492. END InitWriteParams;
  493. (* only data tracks are verified. reading DA is not accurate enough since there is no sync pattern. *)
  494. PROCEDURE VerifyCompilation(compilation: Compilation): LONGINT;
  495. VAR
  496. i: LONGINT;
  497. track: InformationTrack;
  498. BEGIN
  499. (* reloading not possible for notebook drives but LMT is the same in the capability page *)
  500. (*
  501. Unlock();
  502. IF (dev.MediaEject(FALSE, FALSE) # ResOk) OR (dev.MediaEject(FALSE, TRUE) # ResOk) THEN
  503. Abort(); RETURN ResErr;
  504. END;
  505. Lock();
  506. WHILE (~IsReady()) & (~CheckNoMediumPresent()) DO
  507. GetSense(dev);
  508. Objects.Yield();
  509. END;
  510. *)
  511. FOR i := 1 TO compilation.nofTracks-2 DO
  512. track := compilation.tracks[i](InformationTrack);
  513. IF (track.type = DataTrack) & (VerifyTrack(track) # ResOk) THEN RETURN ResErr END;
  514. END;
  515. RETURN ResOk;
  516. END VerifyCompilation;
  517. PROCEDURE VerifyTrack(track: InformationTrack): LONGINT;
  518. VAR
  519. lba, secs, count, nofBlocks, timestamp, ofs: LONGINT;
  520. discBuf: POINTER TO ARRAY OF LONGINT;
  521. fileBuf: POINTER TO ARRAY OF CHAR;
  522. r: Files.Reader;
  523. BEGIN
  524. (*
  525. IF track.type = DataTrack THEN
  526. type := Lib.STMode1;
  527. ELSE
  528. type := Lib.STCdDa;
  529. END;
  530. flags := Lib.HNone + Lib.UserData + Lib.EFNone;
  531. *)
  532. ASSERT(track.type = DataTrack);
  533. dev.SetBlockSize(track.secsize);
  534. timestamp := Kernel.GetTicks ();
  535. NEW(discBuf, track.bytespt DIV 4);
  536. NEW(fileBuf, track.bytespt + 4);
  537. ofs := SYSTEM.VAL (LONGINT, 4 - ADDRESSOF(fileBuf^) MOD 4);
  538. Files.OpenReader(r, track.file, 0);
  539. secs := track.nofsecs; lba := track.startSec;
  540. nofBlocks := track.secspt;
  541. WHILE secs > 0 DO
  542. IF secs < track.secspt THEN
  543. nofBlocks := secs;
  544. END;
  545. IF dev.TransferEx(Disks.Read, lba, nofBlocks, ADDRESSOF(discBuf^), dma & UseDma) # ResOk THEN RETURN ResErr END;
  546. (* IF Lib.ReadCD(dev, lba, nofBlocks, ADDRESSOF(discBuf^), nofBlocks*track.secsize, type, Lib.SCNoData, flags, dma & UseDma) # ResOk THEN RETURN ResErr END; *)
  547. r.Bytes(fileBuf^, ofs, nofBlocks*track.secsize, count);
  548. IF ~CompareData(ADDRESSOF(fileBuf^) + ofs, ADDRESSOF(discBuf^), nofBlocks*track.secsize) THEN RETURN ResErr END;
  549. INC(lba, nofBlocks); DEC(secs, nofBlocks);
  550. INC(recStatus.secsVerified, nofBlocks);
  551. IF Kernel.GetTicks () - timestamp >= NotificationPeriod THEN
  552. StatusChanged();
  553. END;
  554. END;
  555. StatusChanged();
  556. RETURN ResOk;
  557. END VerifyTrack;
  558. PROCEDURE CompareData(adr1, adr2: ADDRESS; len: LONGINT): BOOLEAN;
  559. VAR
  560. i: LONGINT;
  561. BEGIN
  562. FOR i:= 0 TO (len DIV 4) - 1 DO
  563. IF SYSTEM.GET32(adr1) # SYSTEM.GET32(adr2) THEN KernelLog.String("ERROR"); RETURN FALSE END;
  564. INC(adr1, 4); INC(adr2, 4);
  565. END;
  566. RETURN TRUE;
  567. END CompareData;
  568. PROCEDURE Abort;
  569. VAR
  570. size: LONGINT; res: WORD;
  571. BEGIN
  572. Unlock();
  573. (* restore previous blockSize *)
  574. dev.GetSize(size, res);
  575. INCL(dev.flags, Disks.ReadOnly);
  576. IF (reader # NIL) & (reader IS BufferedReader) THEN
  577. reader(BufferedReader).Abort();
  578. END;
  579. reader := NIL;
  580. END Abort;
  581. PROCEDURE Write(op, startSec, nofBlocks: LONGINT; adr: ADDRESS): WORD;
  582. VAR
  583. res: WORD;
  584. BEGIN
  585. LOOP
  586. res := dev.TransferEx(op, startSec, nofBlocks, adr, dma & UseDma);
  587. IF (res # ResOk) & IsInProgress() THEN
  588. timer.Sleep(150);
  589. ELSE
  590. EXIT;
  591. END;
  592. END;
  593. RETURN res;
  594. END Write;
  595. PROCEDURE PadTrack(op: LONGINT; track: InformationTrack) : WORD;
  596. VAR
  597. res: WORD;
  598. startSec, secsToPad, nofBlocks: LONGINT;
  599. buf: POINTER TO ARRAY OF CHAR;
  600. BEGIN
  601. recStatus.operation := Writing;
  602. StatusChanged();
  603. secsToPad := track.pregap;
  604. NEW(buf, track.bytespt);
  605. IF secsToPad >= track.secspt THEN
  606. (* Utils.ClearBuffer(buf^, 0, track.bytespt); *) (* memory is already cleared on allocation *)
  607. ELSE
  608. (* Utils.ClearBuffer(buf^, 0, secsToPad*track.secsize); *) (* memory is already cleared on allocation *)
  609. END;
  610. startSec := track.startSec - track.pregap;
  611. REPEAT
  612. IF secsToPad >= track.secspt THEN
  613. nofBlocks := track.secspt;
  614. ELSE
  615. nofBlocks := secsToPad;
  616. END;
  617. res := Write(op, startSec, nofBlocks, ADDRESSOF(buf^));
  618. IF res # ResOk THEN RETURN ResErr END;
  619. INC(startSec, nofBlocks);
  620. UNTIL startSec >= track.startSec;
  621. RETURN res;
  622. END PadTrack;
  623. PROCEDURE WriteTrack(op: LONGINT; VAR track: InformationTrack) : LONGINT;
  624. VAR
  625. res: WORD; startSec, bytesRead, count, nofBlocks: LONGINT;
  626. timestamp, lastSecs: LONGINT;
  627. buf: Buffer;
  628. BEGIN
  629. lastSecs := recStatus.secsTransferred;
  630. startSec := track.startSec;
  631. recStatus.operation := Writing;
  632. StatusChanged();
  633. timestamp := Kernel.GetTicks ();
  634. REPEAT
  635. count := reader.GetBuffer(buf);
  636. nofBlocks := count DIV track.secsize;
  637. res := Write(op, startSec, nofBlocks, ADDRESSOF(buf^));
  638. IF res # ResOk THEN RETURN ResErr END;
  639. INC(startSec, nofBlocks);
  640. INC(bytesRead, count);
  641. reader.ReleaseBuffer();
  642. INC(recStatus.secsTransferred, nofBlocks);
  643. IF Kernel.GetTicks () - timestamp >= NotificationPeriod THEN
  644. recStatus.currentSpeed := ASH(1000*((RawSectorSize * (recStatus.secsTransferred-lastSecs)) DIV (Kernel.GetTicks () - timestamp)) , -10);
  645. res := GetBufferCapacity(recStatus.bufferSize, recStatus.freeBuffer);
  646. StatusChanged();
  647. timestamp := Kernel.GetTicks (); lastSecs := recStatus.secsTransferred;
  648. END;
  649. UNTIL bytesRead >= track.size;
  650. recStatus.currentSpeed := 0;
  651. recStatus.freeBuffer := recStatus.bufferSize;
  652. RETURN ResOk;
  653. END WriteTrack;
  654. PROCEDURE SetWriteSpeed(speed: LONGINT): WORD;
  655. BEGIN
  656. RETURN Lib.SetCDSpeed(dev, BestSpeed, speed*SingleSpeed, 0H);
  657. END SetWriteSpeed;
  658. PROCEDURE IsReady*(): BOOLEAN;
  659. BEGIN
  660. RETURN dev.TestUnitReady() = ResOk;
  661. END IsReady;
  662. PROCEDURE GetDiscInfo*(disc: Disc): LONGINT;
  663. VAR
  664. info: Lib.DiscInfo;
  665. res: WORD; secsize,secs, nwa: LONGINT;
  666. BEGIN
  667. IF dev.TestUnitReady() # ResOk THEN
  668. res := dev.RequestSense();
  669. IF CheckNoMediumPresent() THEN
  670. RETURN ErrNoMediumPresent;
  671. ELSE
  672. RETURN ErrDriveNotReady;
  673. END;
  674. END;
  675. IF dev.ReadCapacity(secsize, disc.usedBlocks) # ResOk THEN RETURN ResErr END;
  676. IF Lib.ReadDiscInformation(dev, Lib.DTDiscInfoBlock, ADDRESSOF(info), SIZEOF(Lib.DiscInfo)) # ResOk THEN RETURN ResErr END;
  677. disc.erasable := Lib.DIBErasableBit IN SYSTEM.VAL(SET, info.Byte2);
  678. disc.status := Lib.GetField(info.Byte2, Lib.DIBDiscStatusMask, Lib.DIBDiscStatusOfs);
  679. disc.statusLastSession := Lib.GetField(info.Byte2, Lib.DIBSessionStatusMask, Lib.DIBSessionStatusOfs);
  680. disc.nofSessions := ORD(info.NofSessions);
  681. disc.type := ORD(info.DiscType);
  682. disc.latestLeadOut.min := ORD(info.LastLeadOut[1]);
  683. disc.latestLeadOut.sec := ORD(info.LastLeadOut[2]);
  684. disc.latestLeadOut.frame := ORD(info.LastLeadOut[3]);
  685. IF disc.status = Lib.DSComplete THEN
  686. disc.freeBlocks := 0;
  687. ELSE
  688. IF Lib.GetNextAddress(dev, nwa) # ResOk THEN RETURN ResErr END;
  689. MsfToSector(disc.latestLeadOut, secs);
  690. disc.freeBlocks := secs - nwa;
  691. END;
  692. RETURN ResOk;
  693. END GetDiscInfo;
  694. PROCEDURE GetDiscInfoEx*(disc: DiscEx): LONGINT;
  695. VAR
  696. adr: ADDRESS;
  697. res, clvLow, clvHigh: LONGINT;
  698. descr: Lib.ATIPDescriptorPtr;
  699. buf: POINTER TO ARRAY OF CHAR;
  700. BEGIN
  701. res := GetDiscInfo(disc);
  702. IF res # ResOk THEN RETURN res END;
  703. NEW(buf, SIZEOF(Lib.ATIPHeader) + SIZEOF(Lib.ATIPDescriptor));
  704. adr := ADDRESSOF(buf[0]);
  705. IF Lib.ReadToc(dev, FALSE, Lib.TCFormatATIP, 0, adr, LEN(buf^)) # ResOk THEN RETURN ResErr END;
  706. descr := SYSTEM.VAL(Lib.ATIPDescriptorPtr, adr + SIZEOF(Lib.ATIPHeader));
  707. ASSERT(ADDRESSOF(descr^) = SYSTEM.VAL(ADDRESS, descr));
  708. disc.subtype := Lib.GetField(descr.Byte2, Lib.ATSubTypeMask, Lib.ATSubTypeOfs);
  709. IF Lib.ATCdRwBit IN SYSTEM.VAL(SET, descr.Byte2) THEN
  710. disc.refSpeed := Lib.CLVToSpeed(Lib.GetField(descr.Byte0, Lib.ATRefSpeedMask, Lib.ATRefSpeedOfs));
  711. END;
  712. IF Lib.ATA1ValidBit IN SYSTEM.VAL(SET, descr.Byte2) THEN
  713. clvLow := Lib.GetField(descr.A1Values[0], Lib.ATCLVLowMask, Lib.ATCLVLowOfs);
  714. clvHigh := Lib.GetField(descr.A1Values[0], Lib.ATCLVHighMask, Lib.ATCLVHighOfs);
  715. IF clvLow # 0 THEN disc.minSpeed := Lib.CLVToSpeed(clvLow) END;
  716. IF clvHigh # 0 THEN disc.maxSpeed := Lib.CLVToSpeed(clvHigh) END;
  717. IF disc.erasable & (disc.subtype = Lib.ATCdRwHighSpeed) & (clvHigh # 0) THEN
  718. disc.maxSpeed := Lib.CLVToHighSpeed(clvHigh);
  719. END;
  720. END;
  721. IF (Lib.ATA2ValidBit IN SYSTEM.VAL(SET, descr.Byte2)) & disc.erasable & ((disc.subtype = Lib.ATCdRwUltraHighSpeed) OR (disc.subtype = Lib.ATCdRwUltraHighSpeedPlus)) THEN
  722. clvLow := Lib.GetField(descr.A2Values[0], Lib.ATCLVLowMask, Lib.ATCLVLowOfs);
  723. clvHigh := Lib.GetField(descr.A2Values[0], Lib.ATCLVHighMask, Lib.ATCLVHighOfs);
  724. IF (clvLow # 0) THEN disc.minSpeed := Lib.CLVToUltraHighSpeed(clvLow) END;
  725. IF (clvHigh # 0) THEN disc.maxSpeed := Lib.CLVToUltraHighSpeed(clvHigh) END;
  726. END;
  727. RETURN ResOk;
  728. END GetDiscInfoEx;
  729. PROCEDURE GetWriteParams(VAR params: WriteParams): WORD;
  730. VAR
  731. res: WORD;
  732. page: Lib.WriteParameterPage;
  733. BEGIN
  734. res := Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage));
  735. IF res = ResOk THEN
  736. params.writeType := Lib.GetField(page.Byte2, Lib.MPWWriteTypeMask, Lib.MPWWriteTypeOfs);
  737. params.testWrite := Lib.MPWTestWriteBit IN SYSTEM.VAL(SET, page.Byte2);
  738. params.multisession := Lib.GetField(page.Byte3, Lib.MPWMultisessionMask, Lib.MPWMultisessionOfs);
  739. params.trackMode := Lib.GetField(page.Byte3, Lib.MPWTrackModeMask, Lib.MPWTrackModeOfs);
  740. params.bufe := Lib.MPWBufeBit IN SYSTEM.VAL(SET, page.Byte3);
  741. params.DBType := Lib.GetField(page.Byte3, Lib.MPWDataBlockMask, Lib.MPWDataBlockOfs);
  742. END;
  743. RETURN res;
  744. END GetWriteParams;
  745. PROCEDURE SetWriteParams(VAR params: WriteParams; save: BOOLEAN): LONGINT;
  746. VAR
  747. page: Lib.WriteParameterPage;
  748. tmp: WORD;
  749. BEGIN
  750. IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
  751. tmp :=dev.RequestSense(); RETURN ResErr;
  752. END;
  753. Utils.SetBE16(0H, page.Header.DataLength);
  754. EXCL(SYSTEM.VAL(SET, page.Byte0), Lib.MPPSBit);
  755. Lib.SetField(page.Byte2, Lib.MPWWriteTypeMask, Lib.MPWWriteTypeOfs, params.writeType);
  756. IF params.testWrite THEN
  757. Lib.SetBit(page.Byte2, Lib.MPWTestWriteBit);
  758. ELSE
  759. Lib.ClearBit(page.Byte2, Lib.MPWTestWriteBit);
  760. END;
  761. IF params.bufe THEN
  762. Lib.SetBit(page.Byte2, Lib.MPWBufeBit);
  763. ELSE
  764. Lib.ClearBit(page.Byte2, Lib.MPWBufeBit);
  765. END;
  766. Lib.SetField(page.Byte3, Lib.MPWTrackModeMask, Lib.MPWTrackModeOfs, params.trackMode);
  767. Lib.SetField(page.Byte3, Lib.MPWMultisessionMask, Lib.MPWMultisessionOfs, params.multisession);
  768. Lib.SetField(page.Byte4, Lib.MPWDataBlockMask, Lib.MPWDataBlockOfs, params.DBType);
  769. IF Lib.ModeSelect(dev, save, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
  770. tmp := dev.RequestSense(); RETURN ResErr;
  771. END;
  772. RETURN ResOk;
  773. END SetWriteParams;
  774. PROCEDURE PrintWriteParams(): LONGINT;
  775. VAR
  776. page: Lib.WriteParameterPage;
  777. tmp: WORD;
  778. BEGIN
  779. IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
  780. tmp := dev.RequestSense(); RETURN ResErr;
  781. END;
  782. KernelLog.String("Byte 0: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte0), 0, 8); KernelLog.Ln;
  783. KernelLog.String("Page Length: "); KernelLog.Int(ORD(page.Length), 5); KernelLog.Ln;
  784. KernelLog.String("Byte 2: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte2), 0, 8); KernelLog.Ln;
  785. KernelLog.String("Byte 3: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte3), 0, 8); KernelLog.Ln;
  786. KernelLog.String("Byte 4: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte4), 0, 8); KernelLog.Ln;
  787. KernelLog.String("Link Size: "); KernelLog.Int(ORD(page.LinkSize), 5); KernelLog.Ln;
  788. KernelLog.String("Byte7: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte7), 0, 8); KernelLog.Ln;
  789. KernelLog.String("Session Format: "); KernelLog.Int(ORD(page.SessionFormat), 8); KernelLog.Ln;
  790. KernelLog.String("Packet Size: "); KernelLog.Int(Utils.ConvertBE32Int(page.PacketSize), 5); KernelLog.Ln;
  791. KernelLog.String("Audio Pause Length: "); KernelLog.Int(Utils.ConvertBE16Int(page.PauseLength), 5); KernelLog.Ln;
  792. RETURN ResOk;
  793. END PrintWriteParams;
  794. PROCEDURE Lock;
  795. BEGIN
  796. IF dev.MediaLock(TRUE) = ResOk THEN
  797. locked := TRUE;
  798. END;
  799. END Lock;
  800. PROCEDURE Unlock;
  801. BEGIN
  802. IF locked THEN
  803. IF dev.MediaLock(FALSE) = ResOk THEN locked := FALSE END;
  804. END;
  805. END Unlock;
  806. PROCEDURE StatusChanged;
  807. BEGIN
  808. IF onRecordStatusChanged # NIL THEN
  809. onRecordStatusChanged(recStatus);
  810. END;
  811. END StatusChanged;
  812. PROCEDURE CheckIncompatibleMedium(): BOOLEAN;
  813. VAR
  814. msg: ATADisks.GetSenseMsg;
  815. res: WORD;
  816. BEGIN
  817. dev.Handle(msg, res);
  818. RETURN (msg.sense = 5) & (msg.asc = 30);
  819. END CheckIncompatibleMedium;
  820. (* wait until completion of long immediate operations *)
  821. PROCEDURE WaitUntilFinished*;
  822. VAR
  823. msg: ATADisks.GetSenseMsg;
  824. res, tmp: WORD;
  825. timer: Kernel.Timer;
  826. BEGIN
  827. NEW(timer);
  828. REPEAT
  829. timer.Sleep(1000);
  830. res := dev.TestUnitReady();
  831. res := dev.RequestSense();
  832. dev.Handle(msg, tmp);
  833. UNTIL (msg.sense # 2) OR (msg.asc # 4) OR (res # ResOk);
  834. END WaitUntilFinished;
  835. (* check if no medium present *)
  836. PROCEDURE CheckNoMediumPresent*(): BOOLEAN;
  837. VAR
  838. msg: ATADisks.GetSenseMsg;
  839. res: WORD;
  840. BEGIN
  841. dev.Handle(msg, res);
  842. RETURN (msg.sense = 2) & (msg.asc = 3AH) & ((msg.ascq = 0) OR (msg.ascq = 1) OR (msg.ascq = 2));
  843. END CheckNoMediumPresent;
  844. (* long operation in progress *)
  845. PROCEDURE IsInProgress*(): BOOLEAN;
  846. VAR
  847. msg: ATADisks.GetSenseMsg;
  848. res: WORD;
  849. BEGIN
  850. dev.Handle(msg, res);
  851. RETURN (msg.sense = 2) & (msg.asc = 4) & ((msg.ascq = 4) OR (msg.ascq = 7) OR (msg.ascq = 8));
  852. END IsInProgress;
  853. END CDRecorder;
  854. Reader = OBJECT
  855. VAR
  856. compilation: Compilation;
  857. PROCEDURE GetBuffer(VAR buf: Buffer): LONGINT;
  858. (* abstract *)
  859. END GetBuffer;
  860. PROCEDURE ReleaseBuffer;
  861. (* abstract *)
  862. END ReleaseBuffer;
  863. END Reader;
  864. UnbufferedReader = OBJECT(Reader)
  865. VAR
  866. r: Files.Reader;
  867. buffer: Buffer;
  868. trackno, bytesRead: LONGINT;
  869. track: InformationTrack;
  870. PROCEDURE &New*(compilation: Compilation; bufSize: LONGINT);
  871. BEGIN
  872. SELF.compilation := compilation;
  873. r := NIL; track := NIL;
  874. trackno := 0;
  875. NEW(buffer, bufSize);
  876. END New;
  877. PROCEDURE GetBuffer(VAR buf: Buffer): LONGINT;
  878. VAR
  879. amount, rem: LONGINT;
  880. BEGIN
  881. IF (r = NIL) & (track = NIL) THEN
  882. INC(trackno);
  883. track := compilation.tracks[trackno](InformationTrack);
  884. Files.OpenReader(r, track.file, 0);
  885. bytesRead := 0;
  886. END;
  887. amount := 0;
  888. IF r # NIL THEN
  889. r.Bytes(buffer^, 0, track.bytespt, amount);
  890. END;
  891. INC(bytesRead, amount);
  892. IF (amount < track.bytespt) THEN
  893. r := NIL;
  894. rem := Strings.Min(track.bytespt-amount, track.size - bytesRead);
  895. Utils.ClearBuffer(buffer^, amount, rem);
  896. INC(amount, rem); INC(bytesRead, rem);
  897. END;
  898. IF bytesRead >= track.size THEN
  899. track := NIL;
  900. END;
  901. buf := buffer;
  902. RETURN amount;
  903. END GetBuffer;
  904. PROCEDURE ReleaseBuffer;
  905. (* nothing to do for sequential reader *)
  906. END ReleaseBuffer;
  907. END UnbufferedReader;
  908. (* buffer for 1 consumer / 1 producer *)
  909. (* shared variables have single writer *)
  910. ReadBuffer = OBJECT
  911. VAR
  912. buf: Buffer;
  913. len: LONGINT;
  914. PROCEDURE &New*(bufSize: LONGINT);
  915. BEGIN
  916. len := 0;
  917. NEW(buf, bufSize);
  918. END New;
  919. END ReadBuffer;
  920. Fifo = POINTER TO ARRAY OF ReadBuffer;
  921. BufferedReader = OBJECT(Reader)
  922. VAR
  923. nBuffers: LONGINT;
  924. fifo: Fifo;
  925. pIndex, cIndex: LONGINT;
  926. finished: BOOLEAN;
  927. empty*: LONGINT; (* number of times buffer was empty, should be zero *)
  928. aborted: BOOLEAN;
  929. PROCEDURE &New*(compilation: Compilation; bufSize: LONGINT);
  930. VAR
  931. i: LONGINT;
  932. BEGIN
  933. finished := FALSE; empty := 0;
  934. SELF.compilation := compilation;
  935. nBuffers := FifoSize DIV bufSize;
  936. NEW(fifo, nBuffers);
  937. FOR i:=0 TO nBuffers-1 DO
  938. NEW(fifo[i], bufSize);
  939. END;
  940. cIndex := 0; pIndex := 0;
  941. aborted := FALSE;
  942. END New;
  943. PROCEDURE Abort;
  944. BEGIN
  945. aborted := TRUE;
  946. END Abort;
  947. PROCEDURE GetBuffer(VAR buf: Buffer): LONGINT;
  948. VAR
  949. rBuffer: ReadBuffer;
  950. BEGIN
  951. rBuffer := WaitBufferAvailable(TRUE);
  952. buf := rBuffer.buf;
  953. RETURN rBuffer.len;
  954. END GetBuffer;
  955. PROCEDURE Read;
  956. VAR
  957. i: LONGINT;
  958. track: InformationTrack;
  959. BEGIN
  960. FOR i := 1 TO compilation.nofTracks-2 DO
  961. IF aborted THEN RETURN END;
  962. track := compilation.tracks[i](InformationTrack);
  963. ReadTrack(track);
  964. END;
  965. finished := TRUE;
  966. END Read;
  967. PROCEDURE ReadTrack(track: InformationTrack);
  968. VAR
  969. bytesRead, amount, rem: LONGINT;
  970. r: Files.Reader;
  971. rBuffer: ReadBuffer;
  972. BEGIN
  973. Files.OpenReader(r, track.file, 0);
  974. bytesRead := 0;
  975. REPEAT
  976. rBuffer := WaitBufferAvailable(FALSE);
  977. IF aborted THEN RETURN END;
  978. r.Bytes(rBuffer.buf^, 0, track.bytespt, amount);
  979. INC(bytesRead, amount);
  980. IF amount < track.bytespt THEN
  981. rem := Strings.Min(track.bytespt-amount, track.size - bytesRead);
  982. Utils.ClearBuffer(rBuffer.buf^, amount, rem);
  983. INC(amount, rem); INC(bytesRead, rem);
  984. END;
  985. rBuffer.len := amount;
  986. pIndex := (pIndex + 1) MOD nBuffers;
  987. UNTIL bytesRead >= track.file.Length();
  988. WHILE bytesRead < track.size DO
  989. rBuffer := WaitBufferAvailable(FALSE);
  990. amount := Strings.Min(track.bytespt, track.size - bytesRead);
  991. Utils.ClearBuffer(rBuffer.buf^, 0, amount);
  992. rBuffer.len := amount;
  993. INC(bytesRead, amount);
  994. pIndex := (pIndex + 1) MOD nBuffers;
  995. END;
  996. END ReadTrack;
  997. PROCEDURE ReleaseBuffer;
  998. BEGIN
  999. cIndex := (cIndex + 1) MOD nBuffers;
  1000. END ReleaseBuffer;
  1001. (* fill half of buffer *)
  1002. PROCEDURE Init;
  1003. BEGIN
  1004. WHILE (pIndex < nBuffers DIV 2) & ~finished & ~aborted DO
  1005. Objects.Yield();
  1006. END
  1007. END Init;
  1008. PROCEDURE WaitBufferAvailable(read: BOOLEAN): ReadBuffer;
  1009. BEGIN
  1010. IF read THEN
  1011. IF cIndex = pIndex THEN
  1012. INC(empty);
  1013. END;
  1014. WHILE (cIndex = pIndex) DO (* buffer empty *)
  1015. Objects.Yield();
  1016. END;
  1017. RETURN fifo[cIndex];
  1018. ELSE
  1019. WHILE ((pIndex + 1) MOD nBuffers = cIndex) & ~aborted DO
  1020. Objects.Yield();
  1021. END;
  1022. RETURN fifo[pIndex];
  1023. END;
  1024. END WaitBufferAvailable;
  1025. BEGIN {ACTIVE}
  1026. Read();
  1027. END BufferedReader;
  1028. PROCEDURE IdentifyRecorders*(VAR recorders: ARRAY OF CDRecorder): LONGINT;
  1029. VAR
  1030. devTable: Plugins.Table;
  1031. device : ATADisks.DeviceATAPI;
  1032. i, cur: LONGINT;
  1033. cap: Capabilities;
  1034. res : WORD;
  1035. BEGIN
  1036. res := ResErr;
  1037. FOR i := 0 TO MaxRecorders-1 DO
  1038. recorders[i] := NIL;
  1039. END;
  1040. Disks.registry.GetAll(devTable);
  1041. cur := 0;
  1042. IF devTable # NIL THEN
  1043. FOR i := 0 TO LEN(devTable^) - 1 DO
  1044. IF devTable[i] IS ATADisks.DeviceATAPI THEN
  1045. (* KernelLog.String("Identifying"); *)
  1046. device := devTable[i](ATADisks.DeviceATAPI);
  1047. IF GetCapabilities(device, cap) # ResOk THEN RETURN ResErr END;
  1048. IF MFCdr IN cap.mediaFunc THEN
  1049. NEW(recorders[cur], device, cap);
  1050. INC(cur, 1);
  1051. END;
  1052. END;
  1053. END;
  1054. END;
  1055. RETURN ResOk;
  1056. END IdentifyRecorders;
  1057. PROCEDURE GetCapabilities(VAR dev: ATADisks.DeviceATAPI; VAR cap: Capabilities) : LONGINT;
  1058. VAR
  1059. buf : POINTER TO ARRAY OF CHAR;
  1060. ofs, size, numSpeeds, i, curSpeed, maxSpeed, speed, tmp: LONGINT;
  1061. adr: ADDRESS;
  1062. header: Lib.ModeHeader;
  1063. page: Lib.CapabilityPagePtr;
  1064. speedDescr: Lib.SpeedDescriptorPtr;
  1065. feature: Lib.MasteringFeature;
  1066. writeSpeedHeader: Lib.WriteSpeedHeader;
  1067. writeSpeedDescr: Lib.WriteSpeedDescrPtr;
  1068. BEGIN
  1069. (* get mode parameter header to determine size of whole page *)
  1070. IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPCapabilities, ADDRESSOF(header), SIZEOF(Lib.ModeHeader)) # ResOk THEN RETURN ResErr END;
  1071. size := Utils.ConvertBE16Int(header.DataLength)+ 2; (* size of whole page *)
  1072. (* we need an array here since Capability mode page has variable length *)
  1073. NEW(buf, size);
  1074. (* now get whole capabilites page *)
  1075. adr := ADDRESSOF(buf[0]);
  1076. IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPCapabilities, adr, size) # ResOk THEN RETURN ResErr END;
  1077. page := SYSTEM.VAL(Lib.CapabilityPagePtr, adr);
  1078. ASSERT(ADDRESSOF(page^) = SYSTEM.VAL(ADDRESS, page));
  1079. (* Media Functions *)
  1080. IF Lib.MPCCdrBit IN SYSTEM.VAL(SET, page.Byte3) THEN INCL(cap.mediaFunc, MFCdr) END;
  1081. IF Lib.MPCCdRwBit IN SYSTEM.VAL(SET, page.Byte3) THEN INCL(cap.mediaFunc, MFCdRw) END;
  1082. IF Lib.MPCMultisessionBit IN SYSTEM.VAL(SET, page.Byte4) THEN INCL(cap.mediaFunc, MFMultisession) END;
  1083. IF Lib.MPCBufeBit IN SYSTEM.VAL(SET, page.Byte4) THEN INCL(cap.mediaFunc, MFBufe) END;
  1084. CASE Lib.GetField(page.Byte6, Lib.LMTMask, Lib.LMTOfs) OF
  1085. Lib.LMTCaddy: INCL(cap.mediaFunc, MFCaddy);
  1086. | Lib.LMTTray: INCL(cap.mediaFunc, MFTray);
  1087. | Lib.LMTPopUp: INCL(cap.mediaFunc, MFCaddy);
  1088. ELSE
  1089. END;
  1090. numSpeeds := Utils.ConvertBE16Int(page.NofWriteDescriptors);
  1091. (* some drives do not list maximum and current write speed in descriptor table, but fields are actually obsoleted *)
  1092. curSpeed := Utils.ConvertBE16Int(page.CurWriteSpeed2); DEC(curSpeed, curSpeed MOD 2*SingleSpeed);
  1093. maxSpeed := Utils.ConvertBE16Int(page.MaxWriteSpeed); DEC(maxSpeed, maxSpeed MOD 2*SingleSpeed);
  1094. (* get first descriptor *)
  1095. INC(adr, SIZEOF(Lib.CapabilityPage));
  1096. speedDescr := SYSTEM.VAL(Lib.SpeedDescriptorPtr, adr);
  1097. INC(adr, SIZEOF(Lib.SpeedDescriptor));
  1098. speed := Utils.ConvertBE16Int(speedDescr.WriteSpeed);
  1099. IF ListCurrentMaxSpeeds THEN
  1100. IF (maxSpeed > curSpeed) & (curSpeed > speed) THEN
  1101. INC(numSpeeds, 2);
  1102. NEW(cap.writeSpeeds, numSpeeds);
  1103. cap.writeSpeeds[0] := maxSpeed; cap.writeSpeeds[1] := curSpeed; ofs := 2;
  1104. ELSIF maxSpeed > speed THEN
  1105. INC(numSpeeds);
  1106. NEW(cap.writeSpeeds, numSpeeds); cap.writeSpeeds[0] := maxSpeed; ofs := 1;
  1107. ELSE
  1108. NEW(cap.writeSpeeds, numSpeeds); ofs := 0;
  1109. END;
  1110. ELSE
  1111. NEW(cap.writeSpeeds, numSpeeds); ofs := 0;
  1112. END;
  1113. cap.writeSpeeds[ofs] := speed; INC(ofs);
  1114. FOR i := ofs TO numSpeeds-1 DO
  1115. speedDescr := SYSTEM.VAL(Lib.SpeedDescriptorPtr, adr);
  1116. cap.writeSpeeds[i] := Utils.ConvertBE16Int(speedDescr.WriteSpeed);
  1117. INC(adr, SIZEOF(Lib.SpeedDescriptor));
  1118. END;
  1119. (* Get Performance. If we get more speeds here, we use the descriptors provided by this command *)
  1120. (* first only get header *)
  1121. IF Lib.GetPerformance(dev, Lib.PTypeWriteSpeed, 0H, 0H, 0, ADDRESSOF(writeSpeedHeader), SIZEOF(Lib.WriteSpeedHeader)) # ResOk THEN
  1122. size := Utils.ConvertBE32Int(writeSpeedHeader.DataLength) + 4;
  1123. tmp := (size - SIZEOF(Lib.WriteSpeedHeader)) DIV SIZEOF(Lib.WriteSpeedDescr);
  1124. IF tmp > numSpeeds THEN
  1125. numSpeeds := tmp;
  1126. NEW(buf, size);
  1127. adr := ADDRESSOF(buf[0]);
  1128. IF Lib.GetPerformance(dev, Lib.PTypeWriteSpeed, 0H, 0H, numSpeeds, adr, size) # ResOk THEN RETURN ResErr END;
  1129. INC(adr, SIZEOF(Lib.WriteSpeedHeader));
  1130. NEW(cap.writeSpeeds, numSpeeds);
  1131. FOR i:=0 TO numSpeeds-1 DO
  1132. writeSpeedDescr := SYSTEM.VAL(Lib.WriteSpeedDescrPtr, adr);
  1133. cap.writeSpeeds[i] := Utils.ConvertBE32Int(writeSpeedDescr.WriteSpeed);
  1134. INC(adr, SIZEOF(Lib.WriteSpeedDescr));
  1135. END;
  1136. END;
  1137. END;
  1138. (* although spec says that speeds are sorted in descending order, this is not always the case *)
  1139. InsertionSort(cap.writeSpeeds^, LEN(cap.writeSpeeds));
  1140. (* GetConfiguration provides further information *)
  1141. IF Lib.GetConfiguration(dev, Lib.FOne, Lib.FMastering, ADDRESSOF(feature), SIZEOF(Lib.MasteringFeature)) # ResOk THEN RETURN ResErr END;
  1142. IF Lib.FDMSaoBit IN SYSTEM.VAL(SET, feature.Byte4) THEN INCL(cap.mediaFunc, MFSao) END;
  1143. RETURN ResOk;
  1144. END GetCapabilities;
  1145. PROCEDURE InsertionSort(VAR arr: ARRAY OF LONGINT; size: LONGINT);
  1146. VAR
  1147. i, j, index: LONGINT;
  1148. BEGIN
  1149. FOR i := 1 TO size - 1 DO
  1150. index := arr[i]; j := i;
  1151. WHILE (j > 0) & (arr[j-1] < index) DO
  1152. arr[j] := arr[j-1]; DEC(j);
  1153. END;
  1154. arr[j] := index;
  1155. END;
  1156. END InsertionSort;
  1157. PROCEDURE GetSense*(dev: ATADisks.DeviceATAPI);
  1158. VAR
  1159. msg: ATADisks.GetSenseMsg;
  1160. res: WORD;
  1161. BEGIN
  1162. res := dev.RequestSense();
  1163. dev.Handle(msg, res);
  1164. KernelLog.String("sense: "); KernelLog.Hex(msg.sense, 5);
  1165. KernelLog.String(" asc: "); KernelLog.Hex(msg.asc, 5);
  1166. KernelLog.String(" ascq: "); KernelLog.Hex(msg.ascq, 5);
  1167. KernelLog.Ln;
  1168. END GetSense;
  1169. PROCEDURE FileExists(filename: Strings.String): BOOLEAN;
  1170. BEGIN
  1171. RETURN (filename # NIL) & (Files.Old(filename^) # NIL);
  1172. END FileExists;
  1173. PROCEDURE IsIsoFile(filename: Strings.String): BOOLEAN;
  1174. VAR
  1175. info: MakeIsoImages.ISOInfo;
  1176. BEGIN
  1177. NEW(info);
  1178. RETURN info.Open(filename) = ResOk;
  1179. END IsIsoFile;
  1180. PROCEDURE IsWavFile(filename: Strings.String): BOOLEAN;
  1181. VAR
  1182. info: Utils.WAVInfo;
  1183. BEGIN
  1184. NEW(info);
  1185. IF info.Open(filename) = ResOk THEN
  1186. IF (info.nofchannels = 2) & (info.samplerate = 44100) & (info.encoding = 16) THEN
  1187. RETURN TRUE;
  1188. END;
  1189. END;
  1190. RETURN FALSE;
  1191. END IsWavFile;
  1192. PROCEDURE SectorToMsf(sector: LONGINT; VAR msf: MSF);
  1193. VAR
  1194. rem: LONGINT;
  1195. BEGIN
  1196. rem := sector + InitialPregap; (* see Mt. Fuji Table 621 LBA to MSF Translation *)
  1197. msf.min := ENTIER(rem / (60*75));
  1198. DEC(rem, msf.min*60*75);
  1199. msf.sec := ENTIER(rem / 75);
  1200. DEC(rem, msf.sec*75);
  1201. msf.frame := rem;
  1202. END SectorToMsf;
  1203. PROCEDURE MsfToSector(VAR msf: MSF; VAR sector: LONGINT);
  1204. BEGIN
  1205. sector := 75*(60*msf.min + msf.sec) + msf.frame - InitialPregap;
  1206. END MsfToSector;
  1207. PROCEDURE Test*;
  1208. VAR
  1209. res: WORD;
  1210. settings: BurnSettings;
  1211. compilation: Compilation;
  1212. recorders: ARRAY MaxRecorders OF CDRecorder;
  1213. BEGIN
  1214. res := IdentifyRecorders(recorders);
  1215. IF recorders[0] # NIL THEN
  1216. KernelLog.String("Recording");
  1217. (* audio test: wav files must be 16 bit encoded, 44.1kHz and 2 channel*)
  1218. NEW(compilation);
  1219. res := compilation.AddTrack(Strings.NewString("Auto0:/Data/TRACK0.WAV"), AudioTrack, FALSE);
  1220. compilation.Finish();
  1221. (* iso file *)
  1222. (*
  1223. NEW(compilation);
  1224. res := compilation.AddTrack(Strings.NewString("Auto0:/Data/TEST1.ISO"), DataTrack, FALSE);
  1225. Print("res track 1", res);
  1226. compilation.Finish();
  1227. *)
  1228. settings.speed := 16;
  1229. settings.writeType := TrackAtOnce;
  1230. settings.verify := FALSE;
  1231. res :=recorders[0].Record(compilation, settings, PrintStatus);
  1232. ELSE
  1233. KernelLog.String("no ready recorder found");
  1234. END;
  1235. END Test;
  1236. PROCEDURE PrintStatus(status: Utils.Status);
  1237. VAR
  1238. recStatus: RecordingStatus;
  1239. BEGIN
  1240. recStatus := status(RecordingStatus);
  1241. KernelLog.String("current Speed: "); KernelLog.Int(recStatus.currentSpeed, 8); KernelLog.Ln;
  1242. KernelLog.String("secs Transferred: "); KernelLog.Int(recStatus.secsTransferred, 8); KernelLog.Ln;
  1243. KernelLog.String("free Buffer: "); KernelLog.Int(recStatus.freeBuffer, 8); KernelLog.Ln;
  1244. KernelLog.String("buffer Size: "); KernelLog.Int(recStatus.bufferSize, 8); KernelLog.Ln;
  1245. END PrintStatus;
  1246. END CDRecord.
  1247. CDRecord.Test~