12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421 |
- MODULE CDRecord;
- (*
- References:
- Mt. Fuji Commands for Multimedia Devices (SFF8090i v6)
- ECMA-130 Data interchange on read-only 120 mm optical data disks
- *)
- IMPORT SYSTEM, Kernel, ATADisks, Lib := CDRecordLib, Utils := CDRecordUtils, Disks, Plugins, KernelLog, Files, Strings, Objects, MakeIsoImages;
- CONST
- ResOk=0; ResErr=1;
- Debug = FALSE;
- UseDma = TRUE;
- UseBufferedReader = TRUE;
- MaxRecorders* = 2;
- BufferSize = 16*1024*1024;
- RawSectorSize = 2352;
- BlockSize = 2048;
- DataSectorSize = 2048; (* Yellow Book Mode 1 *)
- AudioSectorSize = RawSectorSize;
- TrackLimit = 99;
- TransferSize = 200; (* in sectors *)
- MinTrackSize = 300; (* in sectors *)
- DefaultPregap = 150; (* in sectors *)
- InitialPregap = 150; (* initial pregap which is not accessible with logical addressing *)
- (* incremental writing *)
- NoRunInBlocks = 2;
- NoRunOutBlocks = 1;
- NoLinkBlocks = 4;
- FifoSize = 16*1024*1024;
- ListCurrentMaxSpeeds = FALSE; (* not recommended *)
- NotificationPeriod = 1000; (* client is informed every NotificationPeriod about status *)
- SingleSpeed* = 176; (* kByte/s *)
- (* Burn Settings *)
- TrackAtOnce* = Lib.WTTao; SessionAtOnce* = Lib.WTSao;
- tnoLeadout* = 0AAH; (* track number of lead out*)
- (* Track Types *)
- AudioTrack* = 0; DataTrack* = 1;
- BestSpeed = 0FFFFH;
- (* track properties *)
- (* media functions *)
- MFCdRw* = 0; MFCdr* = 1; MFSao* = 2; MFBufe* = 3; MFMultisession* = 4; MFCaddy* = 5; MFTray* = 6; MFPopup* = 7;
- (* Errors *)
- ErrFileNotFound* = 3000;
- ErrTrackSizeExceeded* = 3001;
- ErrNotEnoughFreeSpace* = 3003;
- ErrNoMediumPresent* = 3004;
- ErrDriveNotReady* = 3005;
- ErrNoIsoFile* = 3006;
- ErrWrongWaveFile* = 3007; (* not an appropriate wav file (must be 16 bit encoded, 44.1kHz and 2 channel) *)
- ErrCalibrationFailed* = 3008;
- ErrDiscNotEmpty* = 3009;
- ErrCDRWNotEmpty* = 3010;
- ErrDiscNotAppendable*= 3011;
- ErrCDRWNotAppendable* = 3012;
- ErrSendingCueSheet* = 3013;
- ErrIncompatibleMedium* = 3014;
- ErrWriting* = 3015;
- ErrVerificationFailed* = 3016;
- (* record operations *)
- Writing* = 0; ClosingTrack* = 1; ClosingSession* = 2; SendingCueSheet* = 3; Calibrating* = 4; FillingFifo* = 5; FlushingCache* = 6; Verifying* = 7;
- TYPE
- Buffer = POINTER TO ARRAY OF CHAR;
- Capabilities* = RECORD
- writeSpeeds* : POINTER TO ARRAY OF LONGINT;
- mediaFunc* : SET;
- END;
- WriteParams = RECORD
- writeType, multisession, trackMode, DBType: LONGINT;
- testWrite, bufe: BOOLEAN;
- END;
- RecordingStatus* = OBJECT(Utils.Status)
- VAR
- currentSpeed*: LONGINT; (* raw speed *)
- freeBuffer*, bufferSize*, operation*, secsTransferred*, secsVerified*: LONGINT;
- empty*: LONGINT; (* number of times sw buffer was empty *)
- END RecordingStatus;
- BurnSettings* = RECORD
- writeType*: LONGINT;
- verify*, bufe*, multisession*, append*: BOOLEAN;
- speed*: LONGINT;
- END;
- Disc* = OBJECT
- VAR
- erasable*: BOOLEAN;
- status*, statusLastSession*, nofSessions*: LONGINT;
- usedBlocks*, freeBlocks*: LONGINT;
- type*: LONGINT;
- latestLeadOut*: MSF;
- END Disc;
- (* extended disc information for CDR/CDRW media*)
- DiscEx* = OBJECT(Disc)
- VAR
- refSpeed*: LONGINT; (* valid only for CDRW *)
- minSpeed*, maxSpeed*: LONGINT;
- subtype*: LONGINT;
- END DiscEx;
- Track = OBJECT
- VAR
- tno, padding, nofsecs, startSec, secsize, type: LONGINT;
- bytespt: LONGINT; (* bytes per transfer *)
- secspt: LONGINT; (* secs per transfer *)
- pregap: LONGINT; (* pregap in sectors *)
- size: LONGINT; (* size of track in bytes *)
- permission: BOOLEAN;
- PROCEDURE &New*(tno, trackType: LONGINT; permitCopy: BOOLEAN);
- BEGIN
- SELF.tno := tno;
- SELF.permission := permitCopy;
- SELF.type := trackType;
- IF type = AudioTrack THEN
- secsize := AudioSectorSize;
- ELSE
- secsize := DataSectorSize;
- END;
- secspt := TransferSize;
- bytespt := secsize*secspt;
- END New;
- END Track;
- InformationTrack* = OBJECT(Track)
- VAR
- file-: Files.File;
- PROCEDURE &NewInfTrack*(tno, trackType: LONGINT; permitCopy: BOOLEAN; file: Files.File);
- BEGIN
- New(tno, trackType, permitCopy);
- SELF.file := file;
- pregap := DefaultPregap;
- InitTrack();
- END NewInfTrack;
- PROCEDURE InitTrack;
- BEGIN
- padding := 0;
- nofsecs := file.Length() DIV secsize;
- IF file.Length() MOD secsize # 0 THEN
- INC(nofsecs);
- padding := secsize - (file.Length() MOD secsize);
- END;
- IF nofsecs < MinTrackSize THEN
- INC(padding, (MinTrackSize - nofsecs) * secsize);
- nofsecs := MinTrackSize;
- END;
- size := nofsecs*secsize;
- END InitTrack;
- END InformationTrack;
- MSF = RECORD
- min, sec, frame: LONGINT;
- END;
- CueSheetEntry = RECORD
- ctladr, tNo, index, dataForm: CHAR;
- scms, min, sec, frame: CHAR;
- END;
- CueSheet = OBJECT
- VAR
- nofEntries, cur: LONGINT;
- adr: ADDRESS;
- buf: POINTER TO ARRAY OF CueSheetEntry;
- PROCEDURE &New*(compilation: Compilation);
- BEGIN
- nofEntries := 2*compilation.nofTracks - 2;
- NEW(buf, nofEntries);
- adr := ADDRESSOF(buf[0]);
- GenerateSheet(compilation.tracks, compilation.nofTracks);
- END New;
- PROCEDURE GenerateSheet(CONST tracks: ARRAY OF Track; nofTracks: LONGINT);
- VAR
- i: LONGINT;
- df: CHAR;
- BEGIN
- FOR i := 0 TO nofTracks - 1 DO
- IF i = 0 THEN (* leadin *)
- Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMLeadin);
- AddEntry(tracks[i], 0, df);
- ELSIF i = nofTracks-1 THEN (* leadout *)
- Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMLeadout);
- AddEntry(tracks[i], 1, df);
- ELSE
- IF tracks[i].type = AudioTrack THEN
- Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMDigitalAudio);
- ELSE
- Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMCdRomMode1);
- END;
- (* we always add index 0 even if there is no pregap*)
- AddEntry(tracks[i], 0, df);
- AddEntry(tracks[i], 1, df);
- END;
- END;
- END GenerateSheet;
- PROCEDURE AddEntry(track: Track; index: LONGINT; df: CHAR);
- VAR
- ctladr: CHAR;
- startSec: LONGINT;
- msf: MSF;
- entry: CueSheetEntry;
- nibble: SET; (* q channel nibble *)
- BEGIN
- startSec := track.startSec;
- IF index = 0 THEN
- DEC(startSec, track.pregap);
- END;
- nibble := {};
- IF track.type = DataTrack THEN INCL(nibble, Lib.QCDataTrack) END;
- IF track.permission THEN INCL(nibble, Lib.QCCopyPermitted) END;
- Lib.SetField(ctladr, Lib.CTLMask, Lib.CTLOfs, SYSTEM.VAL(LONGINT, nibble));
- Lib.SetField(ctladr, Lib.ADRMask, Lib.ADROfs, Lib.ADRTno);
- SectorToMsf(startSec, msf);
- entry.ctladr := ctladr;
- entry.tNo := CHR(track.tno);
- entry.index := CHR(index);
- entry.dataForm := df;
- entry.scms := 0X;
- entry.min := CHR(msf.min);
- entry.sec := CHR(msf.sec);
- entry.frame :=CHR(msf.frame);
- buf[cur] := entry;
- INC(cur);
- END AddEntry;
- PROCEDURE Print;
- VAR
- i: LONGINT;
- entry: CueSheetEntry;
- BEGIN
- KernelLog.Ln;
- FOR i := 0 TO nofEntries - 1 DO
- entry := buf[i];
- KernelLog.Int(ORD(entry.ctladr), 5);
- KernelLog.Int(ORD(entry.tNo), 5);
- KernelLog.Int(ORD(entry.index), 5);
- KernelLog.Int(ORD(entry.dataForm), 5);
- KernelLog.Int(ORD(entry.scms), 5);
- KernelLog.Int(ORD(entry.min), 5);
- KernelLog.Int(ORD(entry.sec), 5);
- KernelLog.Int(ORD(entry.frame), 5);
- KernelLog.Ln;
- END;
- END Print;
- END CueSheet;
- Compilation* = OBJECT
- VAR
- nofTracks-, totalSize-: LONGINT;
- tracks-: ARRAY TrackLimit OF Track;
- PROCEDURE &New*;
- BEGIN
- nofTracks := 0;
- totalSize := -InitialPregap; (* 150 sector pregap of first track is not accessible with logical addressing *)
- END New;
- PROCEDURE Finish*;
- VAR
- i: LONGINT;
- BEGIN
- ASSERT(nofTracks >= 1);
- (* leadin *)
- NEW(tracks[0], 0H, tracks[1].type, tracks[1].permission);
- tracks[0].pregap := InitialPregap;
- INC(nofTracks, 1);
- (* leadout *)
- NEW(tracks[nofTracks], tnoLeadout, tracks[nofTracks-1].type, tracks[nofTracks-1].permission);
- INC(nofTracks, 1);
- (* set the start sector for each track *)
- FOR i := 1 TO nofTracks-1 DO
- INC(totalSize, tracks[i].pregap);
- tracks[i].startSec := totalSize;
- INC(totalSize, tracks[i].nofsecs);
- END;
- END Finish;
- (* Returns the number of total sectors *)
- PROCEDURE GetSize*(raw, secs: BOOLEAN): LONGINT;
- VAR
- i, size: LONGINT;
- BEGIN
- size := 0;
- IF raw THEN (* incl lead in ... *)
- FOR i := 1 TO nofTracks - 1 DO
- IF secs THEN
- INC(size, tracks[i].nofsecs + tracks[i].pregap);
- ELSE
- INC(size, tracks[i].nofsecs * tracks[i].secsize + tracks[i].pregap*tracks[i].secsize);
- END;
- END;
- ELSE
- FOR i := 1 TO nofTracks -2 DO
- IF secs THEN
- INC(size, tracks[i].nofsecs);
- ELSE
- INC(size, tracks[i].nofsecs * tracks[i].secsize);
- END;
- END;
- END;
- RETURN size;
- END GetSize;
- PROCEDURE AddTrack*(filename: Strings.String; trackType : LONGINT; permitCopy: BOOLEAN) : LONGINT;
- VAR
- res: WORD;
- track: InformationTrack;
- file: Files.File;
- BEGIN
- IF nofTracks >= TrackLimit THEN
- RETURN ErrTrackSizeExceeded;
- ELSIF FileExists(filename) THEN
- IF trackType = AudioTrack THEN
- IF ~IsWavFile(filename) THEN
- RETURN ErrWrongWaveFile;
- END;
- ELSE
- IF ~IsIsoFile(filename) THEN
- RETURN ErrNoIsoFile;
- END;
- END;
- INC(nofTracks, 1);
- file := Files.Old(filename^);
- NEW(track, nofTracks, trackType, permitCopy, file);
- tracks[nofTracks] := track;
- res := ResOk;
- ELSE
- res := ErrFileNotFound;
- END;
- RETURN ResOk;
- END AddTrack;
- END Compilation;
- CDRecorder* = OBJECT
- VAR
- cap*: Capabilities;
- dev*: ATADisks.DeviceATAPI;
- recStatus*: RecordingStatus;
- onRecordStatusChanged: Utils.StatusProc;
- name*: ARRAY 128 OF CHAR;
- reader: Reader;
- locked*: BOOLEAN;
- dma*: BOOLEAN;
- timer: Kernel.Timer;
- PROCEDURE &New*(VAR dev: ATADisks.DeviceATAPI; cap: Capabilities);
- BEGIN
- SELF.dev := dev;
- dma := ATADisks.DMABit IN SYSTEM.VAL(SET, dev.id.type);
- SELF.cap := cap;
- COPY(dev.desc, name);
- onRecordStatusChanged := NIL;
- NEW(timer);
- END New;
- PROCEDURE UpdateCapacity*;
- VAR
- size: LONGINT; res: WORD;
- BEGIN
- dev.GetSize(size, res);
- END UpdateCapacity;
- PROCEDURE GetBufferCapacity(VAR totalCapacity, unusedCapacity: LONGINT): WORD;
- VAR
- capacity: Lib.BufferCapacity;
- res: WORD;
- BEGIN
- res := Lib.ReadBufferCapacity(dev, FALSE, ADDRESSOF(capacity), SIZEOF(Lib.BufferCapacity));
- IF res = ResOk THEN
- totalCapacity := Utils.ConvertBE32Int(capacity.BufferLength);
- unusedCapacity := Utils.ConvertBE32Int(capacity.BlankLength);
- END;
- RETURN res;
- END GetBufferCapacity;
- PROCEDURE Record*(VAR compilation: Compilation; settings: BurnSettings; onRecordStatusChanged: Utils.StatusProc): WORD;
- VAR
- disc: Disc;
- res: WORD; i, op, nwa, secs: LONGINT;
- nibble: SET; (* the control nibble of the q channel *)
- params: WriteParams;
- cuesheet: CueSheet;
- track: InformationTrack;
- uReader: UnbufferedReader;
- bufReader: BufferedReader;
- BEGIN {EXCLUSIVE}
- SELF.onRecordStatusChanged := onRecordStatusChanged;
- NEW(recStatus);
- NEW(disc);
- res := GetDiscInfo(disc);
- IF res # ResOk THEN RETURN res END;
- IF settings.append THEN
- IF (disc.status # Lib.DSEmpty) & (disc.status # Lib.DSAppendable) THEN
- IF disc.erasable THEN
- RETURN ErrCDRWNotAppendable;
- ELSE
- RETURN ErrDiscNotAppendable;
- END;
- END;
- ELSE
- IF (disc.status # Lib.DSEmpty) THEN
- IF disc.erasable THEN
- RETURN ErrCDRWNotEmpty;
- ELSE
- RETURN ErrDiscNotEmpty;
- END;
- END;
- END;
- IF compilation.GetSize(TRUE, TRUE) > disc.freeBlocks THEN RETURN ErrNotEnoughFreeSpace END;
- Lock();
- IF SetWriteSpeed(settings.speed) # ResOk THEN Abort(); RETURN ResErr END;
- recStatus.operation := Calibrating; StatusChanged();
- IF Lib.SendOPCInformation(dev, TRUE) # ResOk THEN Abort(); RETURN ErrCalibrationFailed END;
- (* IF settings.verify THEN op := ATADisks.WriteAndVerify ELSE op := Disks.Write END; *)
- op := Disks.Write; (* we only verify after having written the whole compilation *)
- InitWriteParams(settings, params);
- IF UseBufferedReader THEN
- NEW(bufReader, compilation, TransferSize*RawSectorSize);
- recStatus.operation := FillingFifo; StatusChanged();
- bufReader.Init();
- reader := bufReader;
- ELSE
- NEW(uReader, compilation, TransferSize*RawSectorSize);
- reader := uReader;
- END;
- IF settings.writeType = SessionAtOnce THEN
- IF SetWriteParams(params, FALSE) # ResOk THEN Abort(); RETURN ResErr END;
- IF Debug THEN res := PrintWriteParams() END;
- NEW(cuesheet, compilation);
- IF Debug THEN cuesheet.Print() END;
- recStatus.operation := SendingCueSheet; StatusChanged();
- IF Lib.SendCueSheet(dev, cuesheet.adr, cuesheet.nofEntries*SIZEOF(CueSheetEntry)) # ResOk THEN
- res := dev.RequestSense(); Abort(); RETURN ErrSendingCueSheet;
- END;
- (* in case of multisession. but most recorder don't support multisession in sao mode anyway *)
- IF Lib.GetNextAddress(dev, nwa) # ResOk THEN RETURN ResErr END;
- (* some drive return wrong start sec for sao if disc is empty *)
- IF nwa <= 0 THEN nwa := -InitialPregap END;
- IF settings.multisession THEN
- FOR i := 0 TO compilation.nofTracks - 1 DO
- INC(compilation.tracks[i].startSec, nwa + InitialPregap);
- END;
- END;
- END;
- (* Read Capacity returns a capacity of 1 for empty media so we have to set it explicitly*)
- MsfToSector(disc.latestLeadOut, secs);
- dev.SetCapacity(secs);
- EXCL(dev.flags, Disks.ReadOnly);
- FOR i := 1 TO compilation.nofTracks-2 DO
- track := compilation.tracks[i](InformationTrack);
- (* set blockSize *)
- dev.SetBlockSize(track.secsize);
- IF settings.writeType = TrackAtOnce THEN
- nibble := {};
- IF track.type = DataTrack THEN
- INCL(nibble, Lib.QCDataTrack);
- params.DBType := Lib.DBIsoMode1;
- ELSE
- params.DBType := Lib.DBRaw;
- END;
- IF track.permission THEN INCL(nibble, Lib.QCCopyPermitted) END;
- params.trackMode := SYSTEM.VAL(LONGINT, nibble);
- IF (SetWriteParams(params, FALSE) # ResOk) OR (Lib.GetNextAddress(dev, track.startSec) # ResOk) THEN Abort(); RETURN ResErr END;
- IF Debug THEN res := PrintWriteParams() END;
- ELSE
- res := PadTrack(op, track);
- IF res # ResOk THEN Abort(); RETURN ErrWriting END;
- END;
- res := WriteTrack(op, track);
- IF res # ResOk THEN Abort(); RETURN ErrWriting END;
- IF settings.writeType = TrackAtOnce THEN
- (* close Track: Write PMA *)
- recStatus.operation := ClosingTrack; StatusChanged();
- res := Lib.CloseTrackSess(dev, TRUE, Lib.CFTrack, Lib.TRInvisible);
- WaitUntilFinished();
- END;
- END;
- IF settings.writeType = TrackAtOnce THEN
- (* close Session: write PMA to toc *)
- recStatus.operation := ClosingSession; StatusChanged();
- res := Lib.CloseTrackSess(dev, TRUE, Lib.CFSession, Lib.Ignore);
- ELSE
- recStatus.operation := FlushingCache; StatusChanged();
- res := Lib.SynchronizeCache(dev, TRUE);
- END;
- WaitUntilFinished();
- IF settings.verify THEN
- recStatus.operation := Verifying; StatusChanged();
- IF VerifyCompilation(compilation) # ResOk THEN
- IF Debug THEN GetSense(dev) END;
- Abort(); RETURN ErrVerificationFailed
- END;
- END;
- Unlock();
- INCL(dev.flags, Disks.ReadOnly);
- IF (reader # NIL) & (reader IS BufferedReader) THEN
- recStatus.empty := reader(BufferedReader).empty;
- ELSE
- recStatus.empty := -1;
- END;
- reader := NIL;
- RETURN ResOk;
- END Record;
- PROCEDURE InitWriteParams(VAR settings: BurnSettings; VAR params: WriteParams);
- BEGIN
- params.writeType := settings.writeType;
- params.bufe := settings.bufe;
- IF settings.multisession THEN
- params.multisession := Lib.MSNextSessB0;
- ELSE
- params.multisession := Lib.MSNoNextSessNoB0;
- END;
- END InitWriteParams;
- (* only data tracks are verified. reading DA is not accurate enough since there is no sync pattern. *)
- PROCEDURE VerifyCompilation(compilation: Compilation): LONGINT;
- VAR
- i: LONGINT;
- track: InformationTrack;
- BEGIN
- (* reloading not possible for notebook drives but LMT is the same in the capability page *)
- (*
- Unlock();
- IF (dev.MediaEject(FALSE, FALSE) # ResOk) OR (dev.MediaEject(FALSE, TRUE) # ResOk) THEN
- Abort(); RETURN ResErr;
- END;
- Lock();
- WHILE (~IsReady()) & (~CheckNoMediumPresent()) DO
- GetSense(dev);
- Objects.Yield();
- END;
- *)
- FOR i := 1 TO compilation.nofTracks-2 DO
- track := compilation.tracks[i](InformationTrack);
- IF (track.type = DataTrack) & (VerifyTrack(track) # ResOk) THEN RETURN ResErr END;
- END;
- RETURN ResOk;
- END VerifyCompilation;
- PROCEDURE VerifyTrack(track: InformationTrack): LONGINT;
- VAR
- lba, secs, count, nofBlocks, timestamp, ofs: LONGINT;
- discBuf: POINTER TO ARRAY OF LONGINT;
- fileBuf: POINTER TO ARRAY OF CHAR;
- r: Files.Reader;
- BEGIN
- (*
- IF track.type = DataTrack THEN
- type := Lib.STMode1;
- ELSE
- type := Lib.STCdDa;
- END;
- flags := Lib.HNone + Lib.UserData + Lib.EFNone;
- *)
- ASSERT(track.type = DataTrack);
- dev.SetBlockSize(track.secsize);
- timestamp := Kernel.GetTicks ();
- NEW(discBuf, track.bytespt DIV 4);
- NEW(fileBuf, track.bytespt + 4);
- ofs := SYSTEM.VAL (LONGINT, 4 - ADDRESSOF(fileBuf^) MOD 4);
- Files.OpenReader(r, track.file, 0);
- secs := track.nofsecs; lba := track.startSec;
- nofBlocks := track.secspt;
- WHILE secs > 0 DO
- IF secs < track.secspt THEN
- nofBlocks := secs;
- END;
- IF dev.TransferEx(Disks.Read, lba, nofBlocks, ADDRESSOF(discBuf^), dma & UseDma) # ResOk THEN RETURN ResErr END;
- (* IF Lib.ReadCD(dev, lba, nofBlocks, ADDRESSOF(discBuf^), nofBlocks*track.secsize, type, Lib.SCNoData, flags, dma & UseDma) # ResOk THEN RETURN ResErr END; *)
- r.Bytes(fileBuf^, ofs, nofBlocks*track.secsize, count);
- IF ~CompareData(ADDRESSOF(fileBuf^) + ofs, ADDRESSOF(discBuf^), nofBlocks*track.secsize) THEN RETURN ResErr END;
- INC(lba, nofBlocks); DEC(secs, nofBlocks);
- INC(recStatus.secsVerified, nofBlocks);
- IF Kernel.GetTicks () - timestamp >= NotificationPeriod THEN
- StatusChanged();
- END;
- END;
- StatusChanged();
- RETURN ResOk;
- END VerifyTrack;
- PROCEDURE CompareData(adr1, adr2: ADDRESS; len: LONGINT): BOOLEAN;
- VAR
- i: LONGINT;
- BEGIN
- FOR i:= 0 TO (len DIV 4) - 1 DO
- IF SYSTEM.GET32(adr1) # SYSTEM.GET32(adr2) THEN KernelLog.String("ERROR"); RETURN FALSE END;
- INC(adr1, 4); INC(adr2, 4);
- END;
- RETURN TRUE;
- END CompareData;
- PROCEDURE Abort;
- VAR
- size: LONGINT; res: WORD;
- BEGIN
- Unlock();
- (* restore previous blockSize *)
- dev.GetSize(size, res);
- INCL(dev.flags, Disks.ReadOnly);
- IF (reader # NIL) & (reader IS BufferedReader) THEN
- reader(BufferedReader).Abort();
- END;
- reader := NIL;
- END Abort;
- PROCEDURE Write(op, startSec, nofBlocks: LONGINT; adr: ADDRESS): WORD;
- VAR
- res: WORD;
- BEGIN
- LOOP
- res := dev.TransferEx(op, startSec, nofBlocks, adr, dma & UseDma);
- IF (res # ResOk) & IsInProgress() THEN
- timer.Sleep(150);
- ELSE
- EXIT;
- END;
- END;
- RETURN res;
- END Write;
- PROCEDURE PadTrack(op: LONGINT; track: InformationTrack) : WORD;
- VAR
- res: WORD;
- startSec, secsToPad, nofBlocks: LONGINT;
- buf: POINTER TO ARRAY OF CHAR;
- BEGIN
- recStatus.operation := Writing;
- StatusChanged();
- secsToPad := track.pregap;
- NEW(buf, track.bytespt);
- IF secsToPad >= track.secspt THEN
- (* Utils.ClearBuffer(buf^, 0, track.bytespt); *) (* memory is already cleared on allocation *)
- ELSE
- (* Utils.ClearBuffer(buf^, 0, secsToPad*track.secsize); *) (* memory is already cleared on allocation *)
- END;
- startSec := track.startSec - track.pregap;
- REPEAT
- IF secsToPad >= track.secspt THEN
- nofBlocks := track.secspt;
- ELSE
- nofBlocks := secsToPad;
- END;
- res := Write(op, startSec, nofBlocks, ADDRESSOF(buf^));
- IF res # ResOk THEN RETURN ResErr END;
- INC(startSec, nofBlocks);
- UNTIL startSec >= track.startSec;
- RETURN res;
- END PadTrack;
- PROCEDURE WriteTrack(op: LONGINT; VAR track: InformationTrack) : LONGINT;
- VAR
- res: WORD; startSec, bytesRead, count, nofBlocks: LONGINT;
- timestamp, lastSecs: LONGINT;
- buf: Buffer;
- BEGIN
- lastSecs := recStatus.secsTransferred;
- startSec := track.startSec;
- recStatus.operation := Writing;
- StatusChanged();
- timestamp := Kernel.GetTicks ();
- REPEAT
- count := reader.GetBuffer(buf);
- nofBlocks := count DIV track.secsize;
- res := Write(op, startSec, nofBlocks, ADDRESSOF(buf^));
- IF res # ResOk THEN RETURN ResErr END;
- INC(startSec, nofBlocks);
- INC(bytesRead, count);
- reader.ReleaseBuffer();
- INC(recStatus.secsTransferred, nofBlocks);
- IF Kernel.GetTicks () - timestamp >= NotificationPeriod THEN
- recStatus.currentSpeed := ASH(1000*((RawSectorSize * (recStatus.secsTransferred-lastSecs)) DIV (Kernel.GetTicks () - timestamp)) , -10);
- res := GetBufferCapacity(recStatus.bufferSize, recStatus.freeBuffer);
- StatusChanged();
- timestamp := Kernel.GetTicks (); lastSecs := recStatus.secsTransferred;
- END;
- UNTIL bytesRead >= track.size;
- recStatus.currentSpeed := 0;
- recStatus.freeBuffer := recStatus.bufferSize;
- RETURN ResOk;
- END WriteTrack;
- PROCEDURE SetWriteSpeed(speed: LONGINT): WORD;
- BEGIN
- RETURN Lib.SetCDSpeed(dev, BestSpeed, speed*SingleSpeed, 0H);
- END SetWriteSpeed;
- PROCEDURE IsReady*(): BOOLEAN;
- BEGIN
- RETURN dev.TestUnitReady() = ResOk;
- END IsReady;
- PROCEDURE GetDiscInfo*(disc: Disc): LONGINT;
- VAR
- info: Lib.DiscInfo;
- res: WORD; secsize,secs, nwa: LONGINT;
- BEGIN
- IF dev.TestUnitReady() # ResOk THEN
- res := dev.RequestSense();
- IF CheckNoMediumPresent() THEN
- RETURN ErrNoMediumPresent;
- ELSE
- RETURN ErrDriveNotReady;
- END;
- END;
- IF dev.ReadCapacity(secsize, disc.usedBlocks) # ResOk THEN RETURN ResErr END;
- IF Lib.ReadDiscInformation(dev, Lib.DTDiscInfoBlock, ADDRESSOF(info), SIZEOF(Lib.DiscInfo)) # ResOk THEN RETURN ResErr END;
- disc.erasable := Lib.DIBErasableBit IN SYSTEM.VAL(SET, info.Byte2);
- disc.status := Lib.GetField(info.Byte2, Lib.DIBDiscStatusMask, Lib.DIBDiscStatusOfs);
- disc.statusLastSession := Lib.GetField(info.Byte2, Lib.DIBSessionStatusMask, Lib.DIBSessionStatusOfs);
- disc.nofSessions := ORD(info.NofSessions);
- disc.type := ORD(info.DiscType);
- disc.latestLeadOut.min := ORD(info.LastLeadOut[1]);
- disc.latestLeadOut.sec := ORD(info.LastLeadOut[2]);
- disc.latestLeadOut.frame := ORD(info.LastLeadOut[3]);
- IF disc.status = Lib.DSComplete THEN
- disc.freeBlocks := 0;
- ELSE
- IF Lib.GetNextAddress(dev, nwa) # ResOk THEN RETURN ResErr END;
- MsfToSector(disc.latestLeadOut, secs);
- disc.freeBlocks := secs - nwa;
- END;
- RETURN ResOk;
- END GetDiscInfo;
- PROCEDURE GetDiscInfoEx*(disc: DiscEx): LONGINT;
- VAR
- adr: ADDRESS;
- res, clvLow, clvHigh: LONGINT;
- descr: Lib.ATIPDescriptorPtr;
- buf: POINTER TO ARRAY OF CHAR;
- BEGIN
- res := GetDiscInfo(disc);
- IF res # ResOk THEN RETURN res END;
- NEW(buf, SIZEOF(Lib.ATIPHeader) + SIZEOF(Lib.ATIPDescriptor));
- adr := ADDRESSOF(buf[0]);
- IF Lib.ReadToc(dev, FALSE, Lib.TCFormatATIP, 0, adr, LEN(buf^)) # ResOk THEN RETURN ResErr END;
- descr := SYSTEM.VAL(Lib.ATIPDescriptorPtr, adr + SIZEOF(Lib.ATIPHeader));
- ASSERT(ADDRESSOF(descr^) = SYSTEM.VAL(ADDRESS, descr));
- disc.subtype := Lib.GetField(descr.Byte2, Lib.ATSubTypeMask, Lib.ATSubTypeOfs);
- IF Lib.ATCdRwBit IN SYSTEM.VAL(SET, descr.Byte2) THEN
- disc.refSpeed := Lib.CLVToSpeed(Lib.GetField(descr.Byte0, Lib.ATRefSpeedMask, Lib.ATRefSpeedOfs));
- END;
- IF Lib.ATA1ValidBit IN SYSTEM.VAL(SET, descr.Byte2) THEN
- clvLow := Lib.GetField(descr.A1Values[0], Lib.ATCLVLowMask, Lib.ATCLVLowOfs);
- clvHigh := Lib.GetField(descr.A1Values[0], Lib.ATCLVHighMask, Lib.ATCLVHighOfs);
- IF clvLow # 0 THEN disc.minSpeed := Lib.CLVToSpeed(clvLow) END;
- IF clvHigh # 0 THEN disc.maxSpeed := Lib.CLVToSpeed(clvHigh) END;
- IF disc.erasable & (disc.subtype = Lib.ATCdRwHighSpeed) & (clvHigh # 0) THEN
- disc.maxSpeed := Lib.CLVToHighSpeed(clvHigh);
- END;
- END;
- IF (Lib.ATA2ValidBit IN SYSTEM.VAL(SET, descr.Byte2)) & disc.erasable & ((disc.subtype = Lib.ATCdRwUltraHighSpeed) OR (disc.subtype = Lib.ATCdRwUltraHighSpeedPlus)) THEN
- clvLow := Lib.GetField(descr.A2Values[0], Lib.ATCLVLowMask, Lib.ATCLVLowOfs);
- clvHigh := Lib.GetField(descr.A2Values[0], Lib.ATCLVHighMask, Lib.ATCLVHighOfs);
- IF (clvLow # 0) THEN disc.minSpeed := Lib.CLVToUltraHighSpeed(clvLow) END;
- IF (clvHigh # 0) THEN disc.maxSpeed := Lib.CLVToUltraHighSpeed(clvHigh) END;
- END;
- RETURN ResOk;
- END GetDiscInfoEx;
- PROCEDURE GetWriteParams(VAR params: WriteParams): WORD;
- VAR
- res: WORD;
- page: Lib.WriteParameterPage;
- BEGIN
- res := Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage));
- IF res = ResOk THEN
- params.writeType := Lib.GetField(page.Byte2, Lib.MPWWriteTypeMask, Lib.MPWWriteTypeOfs);
- params.testWrite := Lib.MPWTestWriteBit IN SYSTEM.VAL(SET, page.Byte2);
- params.multisession := Lib.GetField(page.Byte3, Lib.MPWMultisessionMask, Lib.MPWMultisessionOfs);
- params.trackMode := Lib.GetField(page.Byte3, Lib.MPWTrackModeMask, Lib.MPWTrackModeOfs);
- params.bufe := Lib.MPWBufeBit IN SYSTEM.VAL(SET, page.Byte3);
- params.DBType := Lib.GetField(page.Byte3, Lib.MPWDataBlockMask, Lib.MPWDataBlockOfs);
- END;
- RETURN res;
- END GetWriteParams;
- PROCEDURE SetWriteParams(VAR params: WriteParams; save: BOOLEAN): LONGINT;
- VAR
- page: Lib.WriteParameterPage;
- tmp: WORD;
- BEGIN
- IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
- tmp :=dev.RequestSense(); RETURN ResErr;
- END;
- Utils.SetBE16(0H, page.Header.DataLength);
- EXCL(SYSTEM.VAL(SET, page.Byte0), Lib.MPPSBit);
- Lib.SetField(page.Byte2, Lib.MPWWriteTypeMask, Lib.MPWWriteTypeOfs, params.writeType);
- IF params.testWrite THEN
- Lib.SetBit(page.Byte2, Lib.MPWTestWriteBit);
- ELSE
- Lib.ClearBit(page.Byte2, Lib.MPWTestWriteBit);
- END;
- IF params.bufe THEN
- Lib.SetBit(page.Byte2, Lib.MPWBufeBit);
- ELSE
- Lib.ClearBit(page.Byte2, Lib.MPWBufeBit);
- END;
- Lib.SetField(page.Byte3, Lib.MPWTrackModeMask, Lib.MPWTrackModeOfs, params.trackMode);
- Lib.SetField(page.Byte3, Lib.MPWMultisessionMask, Lib.MPWMultisessionOfs, params.multisession);
- Lib.SetField(page.Byte4, Lib.MPWDataBlockMask, Lib.MPWDataBlockOfs, params.DBType);
- IF Lib.ModeSelect(dev, save, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
- tmp := dev.RequestSense(); RETURN ResErr;
- END;
- RETURN ResOk;
- END SetWriteParams;
- PROCEDURE PrintWriteParams(): LONGINT;
- VAR
- page: Lib.WriteParameterPage;
- tmp: WORD;
- BEGIN
- IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, ADDRESSOF(page), SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
- tmp := dev.RequestSense(); RETURN ResErr;
- END;
- KernelLog.String("Byte 0: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte0), 0, 8); KernelLog.Ln;
- KernelLog.String("Page Length: "); KernelLog.Int(ORD(page.Length), 5); KernelLog.Ln;
- KernelLog.String("Byte 2: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte2), 0, 8); KernelLog.Ln;
- KernelLog.String("Byte 3: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte3), 0, 8); KernelLog.Ln;
- KernelLog.String("Byte 4: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte4), 0, 8); KernelLog.Ln;
- KernelLog.String("Link Size: "); KernelLog.Int(ORD(page.LinkSize), 5); KernelLog.Ln;
- KernelLog.String("Byte7: "); KernelLog.Bits(SYSTEM.VAL(SET, page.Byte7), 0, 8); KernelLog.Ln;
- KernelLog.String("Session Format: "); KernelLog.Int(ORD(page.SessionFormat), 8); KernelLog.Ln;
- KernelLog.String("Packet Size: "); KernelLog.Int(Utils.ConvertBE32Int(page.PacketSize), 5); KernelLog.Ln;
- KernelLog.String("Audio Pause Length: "); KernelLog.Int(Utils.ConvertBE16Int(page.PauseLength), 5); KernelLog.Ln;
- RETURN ResOk;
- END PrintWriteParams;
- PROCEDURE Lock;
- BEGIN
- IF dev.MediaLock(TRUE) = ResOk THEN
- locked := TRUE;
- END;
- END Lock;
- PROCEDURE Unlock;
- BEGIN
- IF locked THEN
- IF dev.MediaLock(FALSE) = ResOk THEN locked := FALSE END;
- END;
- END Unlock;
- PROCEDURE StatusChanged;
- BEGIN
- IF onRecordStatusChanged # NIL THEN
- onRecordStatusChanged(recStatus);
- END;
- END StatusChanged;
- PROCEDURE CheckIncompatibleMedium(): BOOLEAN;
- VAR
- msg: ATADisks.GetSenseMsg;
- res: WORD;
- BEGIN
- dev.Handle(msg, res);
- RETURN (msg.sense = 5) & (msg.asc = 30);
- END CheckIncompatibleMedium;
- (* wait until completion of long immediate operations *)
- PROCEDURE WaitUntilFinished*;
- VAR
- msg: ATADisks.GetSenseMsg;
- res, tmp: WORD;
- timer: Kernel.Timer;
- BEGIN
- NEW(timer);
- REPEAT
- timer.Sleep(1000);
- res := dev.TestUnitReady();
- res := dev.RequestSense();
- dev.Handle(msg, tmp);
- UNTIL (msg.sense # 2) OR (msg.asc # 4) OR (res # ResOk);
- END WaitUntilFinished;
- (* check if no medium present *)
- PROCEDURE CheckNoMediumPresent*(): BOOLEAN;
- VAR
- msg: ATADisks.GetSenseMsg;
- res: WORD;
- BEGIN
- dev.Handle(msg, res);
- RETURN (msg.sense = 2) & (msg.asc = 3AH) & ((msg.ascq = 0) OR (msg.ascq = 1) OR (msg.ascq = 2));
- END CheckNoMediumPresent;
- (* long operation in progress *)
- PROCEDURE IsInProgress*(): BOOLEAN;
- VAR
- msg: ATADisks.GetSenseMsg;
- res: WORD;
- BEGIN
- dev.Handle(msg, res);
- RETURN (msg.sense = 2) & (msg.asc = 4) & ((msg.ascq = 4) OR (msg.ascq = 7) OR (msg.ascq = 8));
- END IsInProgress;
- END CDRecorder;
- Reader = OBJECT
- VAR
- compilation: Compilation;
- PROCEDURE GetBuffer(VAR buf: Buffer): LONGINT;
- (* abstract *)
- END GetBuffer;
- PROCEDURE ReleaseBuffer;
- (* abstract *)
- END ReleaseBuffer;
- END Reader;
- UnbufferedReader = OBJECT(Reader)
- VAR
- r: Files.Reader;
- buffer: Buffer;
- trackno, bytesRead: LONGINT;
- track: InformationTrack;
- PROCEDURE &New*(compilation: Compilation; bufSize: LONGINT);
- BEGIN
- SELF.compilation := compilation;
- r := NIL; track := NIL;
- trackno := 0;
- NEW(buffer, bufSize);
- END New;
- PROCEDURE GetBuffer(VAR buf: Buffer): LONGINT;
- VAR
- amount, rem: LONGINT;
- BEGIN
- IF (r = NIL) & (track = NIL) THEN
- INC(trackno);
- track := compilation.tracks[trackno](InformationTrack);
- Files.OpenReader(r, track.file, 0);
- bytesRead := 0;
- END;
- amount := 0;
- IF r # NIL THEN
- r.Bytes(buffer^, 0, track.bytespt, amount);
- END;
- INC(bytesRead, amount);
- IF (amount < track.bytespt) THEN
- r := NIL;
- rem := Strings.Min(track.bytespt-amount, track.size - bytesRead);
- Utils.ClearBuffer(buffer^, amount, rem);
- INC(amount, rem); INC(bytesRead, rem);
- END;
- IF bytesRead >= track.size THEN
- track := NIL;
- END;
- buf := buffer;
- RETURN amount;
- END GetBuffer;
- PROCEDURE ReleaseBuffer;
- (* nothing to do for sequential reader *)
- END ReleaseBuffer;
- END UnbufferedReader;
- (* buffer for 1 consumer / 1 producer *)
- (* shared variables have single writer *)
- ReadBuffer = OBJECT
- VAR
- buf: Buffer;
- len: LONGINT;
- PROCEDURE &New*(bufSize: LONGINT);
- BEGIN
- len := 0;
- NEW(buf, bufSize);
- END New;
- END ReadBuffer;
- Fifo = POINTER TO ARRAY OF ReadBuffer;
- BufferedReader = OBJECT(Reader)
- VAR
- nBuffers: LONGINT;
- fifo: Fifo;
- pIndex, cIndex: LONGINT;
- finished: BOOLEAN;
- empty*: LONGINT; (* number of times buffer was empty, should be zero *)
- aborted: BOOLEAN;
- PROCEDURE &New*(compilation: Compilation; bufSize: LONGINT);
- VAR
- i: LONGINT;
- BEGIN
- finished := FALSE; empty := 0;
- SELF.compilation := compilation;
- nBuffers := FifoSize DIV bufSize;
- NEW(fifo, nBuffers);
- FOR i:=0 TO nBuffers-1 DO
- NEW(fifo[i], bufSize);
- END;
- cIndex := 0; pIndex := 0;
- aborted := FALSE;
- END New;
- PROCEDURE Abort;
- BEGIN
- aborted := TRUE;
- END Abort;
- PROCEDURE GetBuffer(VAR buf: Buffer): LONGINT;
- VAR
- rBuffer: ReadBuffer;
- BEGIN
- rBuffer := WaitBufferAvailable(TRUE);
- buf := rBuffer.buf;
- RETURN rBuffer.len;
- END GetBuffer;
- PROCEDURE Read;
- VAR
- i: LONGINT;
- track: InformationTrack;
- BEGIN
- FOR i := 1 TO compilation.nofTracks-2 DO
- IF aborted THEN RETURN END;
- track := compilation.tracks[i](InformationTrack);
- ReadTrack(track);
- END;
- finished := TRUE;
- END Read;
- PROCEDURE ReadTrack(track: InformationTrack);
- VAR
- bytesRead, amount, rem: LONGINT;
- r: Files.Reader;
- rBuffer: ReadBuffer;
- BEGIN
- Files.OpenReader(r, track.file, 0);
- bytesRead := 0;
- REPEAT
- rBuffer := WaitBufferAvailable(FALSE);
- IF aborted THEN RETURN END;
- r.Bytes(rBuffer.buf^, 0, track.bytespt, amount);
- INC(bytesRead, amount);
- IF amount < track.bytespt THEN
- rem := Strings.Min(track.bytespt-amount, track.size - bytesRead);
- Utils.ClearBuffer(rBuffer.buf^, amount, rem);
- INC(amount, rem); INC(bytesRead, rem);
- END;
- rBuffer.len := amount;
- pIndex := (pIndex + 1) MOD nBuffers;
- UNTIL bytesRead >= track.file.Length();
- WHILE bytesRead < track.size DO
- rBuffer := WaitBufferAvailable(FALSE);
- amount := Strings.Min(track.bytespt, track.size - bytesRead);
- Utils.ClearBuffer(rBuffer.buf^, 0, amount);
- rBuffer.len := amount;
- INC(bytesRead, amount);
- pIndex := (pIndex + 1) MOD nBuffers;
- END;
- END ReadTrack;
- PROCEDURE ReleaseBuffer;
- BEGIN
- cIndex := (cIndex + 1) MOD nBuffers;
- END ReleaseBuffer;
- (* fill half of buffer *)
- PROCEDURE Init;
- BEGIN
- WHILE (pIndex < nBuffers DIV 2) & ~finished & ~aborted DO
- Objects.Yield();
- END
- END Init;
- PROCEDURE WaitBufferAvailable(read: BOOLEAN): ReadBuffer;
- BEGIN
- IF read THEN
- IF cIndex = pIndex THEN
- INC(empty);
- END;
- WHILE (cIndex = pIndex) DO (* buffer empty *)
- Objects.Yield();
- END;
- RETURN fifo[cIndex];
- ELSE
- WHILE ((pIndex + 1) MOD nBuffers = cIndex) & ~aborted DO
- Objects.Yield();
- END;
- RETURN fifo[pIndex];
- END;
- END WaitBufferAvailable;
- BEGIN {ACTIVE}
- Read();
- END BufferedReader;
- PROCEDURE IdentifyRecorders*(VAR recorders: ARRAY OF CDRecorder): LONGINT;
- VAR
- devTable: Plugins.Table;
- device : ATADisks.DeviceATAPI;
- i, cur: LONGINT;
- cap: Capabilities;
- res : WORD;
- BEGIN
- res := ResErr;
- FOR i := 0 TO MaxRecorders-1 DO
- recorders[i] := NIL;
- END;
- Disks.registry.GetAll(devTable);
- cur := 0;
- IF devTable # NIL THEN
- FOR i := 0 TO LEN(devTable^) - 1 DO
- IF devTable[i] IS ATADisks.DeviceATAPI THEN
- (* KernelLog.String("Identifying"); *)
- device := devTable[i](ATADisks.DeviceATAPI);
- IF GetCapabilities(device, cap) # ResOk THEN RETURN ResErr END;
- IF MFCdr IN cap.mediaFunc THEN
- NEW(recorders[cur], device, cap);
- INC(cur, 1);
- END;
- END;
- END;
- END;
- RETURN ResOk;
- END IdentifyRecorders;
- PROCEDURE GetCapabilities(VAR dev: ATADisks.DeviceATAPI; VAR cap: Capabilities) : LONGINT;
- VAR
- buf : POINTER TO ARRAY OF CHAR;
- ofs, size, numSpeeds, i, curSpeed, maxSpeed, speed, tmp: LONGINT;
- adr: ADDRESS;
- header: Lib.ModeHeader;
- page: Lib.CapabilityPagePtr;
- speedDescr: Lib.SpeedDescriptorPtr;
- feature: Lib.MasteringFeature;
- writeSpeedHeader: Lib.WriteSpeedHeader;
- writeSpeedDescr: Lib.WriteSpeedDescrPtr;
- BEGIN
- (* get mode parameter header to determine size of whole page *)
- IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPCapabilities, ADDRESSOF(header), SIZEOF(Lib.ModeHeader)) # ResOk THEN RETURN ResErr END;
- size := Utils.ConvertBE16Int(header.DataLength)+ 2; (* size of whole page *)
- (* we need an array here since Capability mode page has variable length *)
- NEW(buf, size);
- (* now get whole capabilites page *)
- adr := ADDRESSOF(buf[0]);
- IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPCapabilities, adr, size) # ResOk THEN RETURN ResErr END;
- page := SYSTEM.VAL(Lib.CapabilityPagePtr, adr);
- ASSERT(ADDRESSOF(page^) = SYSTEM.VAL(ADDRESS, page));
- (* Media Functions *)
- IF Lib.MPCCdrBit IN SYSTEM.VAL(SET, page.Byte3) THEN INCL(cap.mediaFunc, MFCdr) END;
- IF Lib.MPCCdRwBit IN SYSTEM.VAL(SET, page.Byte3) THEN INCL(cap.mediaFunc, MFCdRw) END;
- IF Lib.MPCMultisessionBit IN SYSTEM.VAL(SET, page.Byte4) THEN INCL(cap.mediaFunc, MFMultisession) END;
- IF Lib.MPCBufeBit IN SYSTEM.VAL(SET, page.Byte4) THEN INCL(cap.mediaFunc, MFBufe) END;
- CASE Lib.GetField(page.Byte6, Lib.LMTMask, Lib.LMTOfs) OF
- Lib.LMTCaddy: INCL(cap.mediaFunc, MFCaddy);
- | Lib.LMTTray: INCL(cap.mediaFunc, MFTray);
- | Lib.LMTPopUp: INCL(cap.mediaFunc, MFCaddy);
- ELSE
- END;
- numSpeeds := Utils.ConvertBE16Int(page.NofWriteDescriptors);
- (* some drives do not list maximum and current write speed in descriptor table, but fields are actually obsoleted *)
- curSpeed := Utils.ConvertBE16Int(page.CurWriteSpeed2); DEC(curSpeed, curSpeed MOD 2*SingleSpeed);
- maxSpeed := Utils.ConvertBE16Int(page.MaxWriteSpeed); DEC(maxSpeed, maxSpeed MOD 2*SingleSpeed);
- (* get first descriptor *)
- INC(adr, SIZEOF(Lib.CapabilityPage));
- speedDescr := SYSTEM.VAL(Lib.SpeedDescriptorPtr, adr);
- INC(adr, SIZEOF(Lib.SpeedDescriptor));
- speed := Utils.ConvertBE16Int(speedDescr.WriteSpeed);
- IF ListCurrentMaxSpeeds THEN
- IF (maxSpeed > curSpeed) & (curSpeed > speed) THEN
- INC(numSpeeds, 2);
- NEW(cap.writeSpeeds, numSpeeds);
- cap.writeSpeeds[0] := maxSpeed; cap.writeSpeeds[1] := curSpeed; ofs := 2;
- ELSIF maxSpeed > speed THEN
- INC(numSpeeds);
- NEW(cap.writeSpeeds, numSpeeds); cap.writeSpeeds[0] := maxSpeed; ofs := 1;
- ELSE
- NEW(cap.writeSpeeds, numSpeeds); ofs := 0;
- END;
- ELSE
- NEW(cap.writeSpeeds, numSpeeds); ofs := 0;
- END;
- cap.writeSpeeds[ofs] := speed; INC(ofs);
- FOR i := ofs TO numSpeeds-1 DO
- speedDescr := SYSTEM.VAL(Lib.SpeedDescriptorPtr, adr);
- cap.writeSpeeds[i] := Utils.ConvertBE16Int(speedDescr.WriteSpeed);
- INC(adr, SIZEOF(Lib.SpeedDescriptor));
- END;
- (* Get Performance. If we get more speeds here, we use the descriptors provided by this command *)
- (* first only get header *)
- IF Lib.GetPerformance(dev, Lib.PTypeWriteSpeed, 0H, 0H, 0, ADDRESSOF(writeSpeedHeader), SIZEOF(Lib.WriteSpeedHeader)) # ResOk THEN
- size := Utils.ConvertBE32Int(writeSpeedHeader.DataLength) + 4;
- tmp := (size - SIZEOF(Lib.WriteSpeedHeader)) DIV SIZEOF(Lib.WriteSpeedDescr);
- IF tmp > numSpeeds THEN
- numSpeeds := tmp;
- NEW(buf, size);
- adr := ADDRESSOF(buf[0]);
- IF Lib.GetPerformance(dev, Lib.PTypeWriteSpeed, 0H, 0H, numSpeeds, adr, size) # ResOk THEN RETURN ResErr END;
- INC(adr, SIZEOF(Lib.WriteSpeedHeader));
- NEW(cap.writeSpeeds, numSpeeds);
- FOR i:=0 TO numSpeeds-1 DO
- writeSpeedDescr := SYSTEM.VAL(Lib.WriteSpeedDescrPtr, adr);
- cap.writeSpeeds[i] := Utils.ConvertBE32Int(writeSpeedDescr.WriteSpeed);
- INC(adr, SIZEOF(Lib.WriteSpeedDescr));
- END;
- END;
- END;
- (* although spec says that speeds are sorted in descending order, this is not always the case *)
- InsertionSort(cap.writeSpeeds^, LEN(cap.writeSpeeds));
- (* GetConfiguration provides further information *)
- IF Lib.GetConfiguration(dev, Lib.FOne, Lib.FMastering, ADDRESSOF(feature), SIZEOF(Lib.MasteringFeature)) # ResOk THEN RETURN ResErr END;
- IF Lib.FDMSaoBit IN SYSTEM.VAL(SET, feature.Byte4) THEN INCL(cap.mediaFunc, MFSao) END;
- RETURN ResOk;
- END GetCapabilities;
- PROCEDURE InsertionSort(VAR arr: ARRAY OF LONGINT; size: LONGINT);
- VAR
- i, j, index: LONGINT;
- BEGIN
- FOR i := 1 TO size - 1 DO
- index := arr[i]; j := i;
- WHILE (j > 0) & (arr[j-1] < index) DO
- arr[j] := arr[j-1]; DEC(j);
- END;
- arr[j] := index;
- END;
- END InsertionSort;
- PROCEDURE GetSense*(dev: ATADisks.DeviceATAPI);
- VAR
- msg: ATADisks.GetSenseMsg;
- res: WORD;
- BEGIN
- res := dev.RequestSense();
- dev.Handle(msg, res);
- KernelLog.String("sense: "); KernelLog.Hex(msg.sense, 5);
- KernelLog.String(" asc: "); KernelLog.Hex(msg.asc, 5);
- KernelLog.String(" ascq: "); KernelLog.Hex(msg.ascq, 5);
- KernelLog.Ln;
- END GetSense;
- PROCEDURE FileExists(filename: Strings.String): BOOLEAN;
- BEGIN
- RETURN (filename # NIL) & (Files.Old(filename^) # NIL);
- END FileExists;
- PROCEDURE IsIsoFile(filename: Strings.String): BOOLEAN;
- VAR
- info: MakeIsoImages.ISOInfo;
- BEGIN
- NEW(info);
- RETURN info.Open(filename) = ResOk;
- END IsIsoFile;
- PROCEDURE IsWavFile(filename: Strings.String): BOOLEAN;
- VAR
- info: Utils.WAVInfo;
- BEGIN
- NEW(info);
- IF info.Open(filename) = ResOk THEN
- IF (info.nofchannels = 2) & (info.samplerate = 44100) & (info.encoding = 16) THEN
- RETURN TRUE;
- END;
- END;
- RETURN FALSE;
- END IsWavFile;
- PROCEDURE SectorToMsf(sector: LONGINT; VAR msf: MSF);
- VAR
- rem: LONGINT;
- BEGIN
- rem := sector + InitialPregap; (* see Mt. Fuji Table 621 LBA to MSF Translation *)
- msf.min := ENTIER(rem / (60*75));
- DEC(rem, msf.min*60*75);
- msf.sec := ENTIER(rem / 75);
- DEC(rem, msf.sec*75);
- msf.frame := rem;
- END SectorToMsf;
- PROCEDURE MsfToSector(VAR msf: MSF; VAR sector: LONGINT);
- BEGIN
- sector := 75*(60*msf.min + msf.sec) + msf.frame - InitialPregap;
- END MsfToSector;
- PROCEDURE Test*;
- VAR
- res: WORD;
- settings: BurnSettings;
- compilation: Compilation;
- recorders: ARRAY MaxRecorders OF CDRecorder;
- BEGIN
- res := IdentifyRecorders(recorders);
- IF recorders[0] # NIL THEN
- KernelLog.String("Recording");
- (* audio test: wav files must be 16 bit encoded, 44.1kHz and 2 channel*)
- NEW(compilation);
- res := compilation.AddTrack(Strings.NewString("Auto0:/Data/TRACK0.WAV"), AudioTrack, FALSE);
- compilation.Finish();
- (* iso file *)
- (*
- NEW(compilation);
- res := compilation.AddTrack(Strings.NewString("Auto0:/Data/TEST1.ISO"), DataTrack, FALSE);
- Print("res track 1", res);
- compilation.Finish();
- *)
- settings.speed := 16;
- settings.writeType := TrackAtOnce;
- settings.verify := FALSE;
- res :=recorders[0].Record(compilation, settings, PrintStatus);
- ELSE
- KernelLog.String("no ready recorder found");
- END;
- END Test;
- PROCEDURE PrintStatus(status: Utils.Status);
- VAR
- recStatus: RecordingStatus;
- BEGIN
- recStatus := status(RecordingStatus);
- KernelLog.String("current Speed: "); KernelLog.Int(recStatus.currentSpeed, 8); KernelLog.Ln;
- KernelLog.String("secs Transferred: "); KernelLog.Int(recStatus.secsTransferred, 8); KernelLog.Ln;
- KernelLog.String("free Buffer: "); KernelLog.Int(recStatus.freeBuffer, 8); KernelLog.Ln;
- KernelLog.String("buffer Size: "); KernelLog.Int(recStatus.bufferSize, 8); KernelLog.Ln;
- END PrintStatus;
- END CDRecord.
- CDRecord.Test~
|