MODULE CDRecord;
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;
AudioSectorSize = RawSectorSize;
TrackLimit = 99;
TransferSize = 200;
MinTrackSize = 300;
DefaultPregap = 150;
InitialPregap = 150;
NoRunInBlocks = 2;
NoRunOutBlocks = 1;
NoLinkBlocks = 4;
FifoSize = 16*1024*1024;
ListCurrentMaxSpeeds = FALSE;
NotificationPeriod = 1000;
SingleSpeed* = 176;
TrackAtOnce* = Lib.WTTao; SessionAtOnce* = Lib.WTSao;
tnoLeadout* = 0AAH;
AudioTrack* = 0; DataTrack* = 1;
BestSpeed = 0FFFFH;
MFCdRw* = 0; MFCdr* = 1; MFSao* = 2; MFBufe* = 3; MFMultisession* = 4; MFCaddy* = 5; MFTray* = 6; MFPopup* = 7;
ErrFileNotFound* = 3000;
ErrTrackSizeExceeded* = 3001;
ErrNotEnoughFreeSpace* = 3003;
ErrNoMediumPresent* = 3004;
ErrDriveNotReady* = 3005;
ErrNoIsoFile* = 3006;
ErrWrongWaveFile* = 3007;
ErrCalibrationFailed* = 3008;
ErrDiscNotEmpty* = 3009;
ErrCDRWNotEmpty* = 3010;
ErrDiscNotAppendable*= 3011;
ErrCDRWNotAppendable* = 3012;
ErrSendingCueSheet* = 3013;
ErrIncompatibleMedium* = 3014;
ErrWriting* = 3015;
ErrVerificationFailed* = 3016;
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;
freeBuffer*, bufferSize*, operation*, secsTransferred*, secsVerified*: LONGINT;
empty*: LONGINT;
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;
DiscEx* = OBJECT(Disc)
VAR
refSpeed*: LONGINT;
minSpeed*, maxSpeed*: LONGINT;
subtype*: LONGINT;
END DiscEx;
Track = OBJECT
VAR
tno, padding, nofsecs, startSec, secsize, type: LONGINT;
bytespt: LONGINT;
secspt: LONGINT;
pregap: LONGINT;
size: LONGINT;
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: SYSTEM.ADDRESS;
buf: POINTER TO ARRAY OF CueSheetEntry;
PROCEDURE &New*(compilation: Compilation);
BEGIN
nofEntries := 2*compilation.nofTracks - 2;
NEW(buf, nofEntries);
adr := SYSTEM.ADR(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
Lib.SetField(df, Lib.DFMMask, Lib.DFMOfs, Lib.DFMLeadin);
AddEntry(tracks[i], 0, df);
ELSIF i = nofTracks-1 THEN
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;
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;
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;
END New;
PROCEDURE Finish*;
VAR
i: LONGINT;
BEGIN
ASSERT(nofTracks >= 1);
NEW(tracks[0], 0H, tracks[1].type, tracks[1].permission);
tracks[0].pregap := InitialPregap;
INC(nofTracks, 1);
NEW(tracks[nofTracks], tnoLeadout, tracks[nofTracks-1].type, tracks[nofTracks-1].permission);
INC(nofTracks, 1);
FOR i := 1 TO nofTracks-1 DO
INC(totalSize, tracks[i].pregap);
tracks[i].startSec := totalSize;
INC(totalSize, tracks[i].nofsecs);
END;
END Finish;
PROCEDURE GetSize*(raw, secs: BOOLEAN): LONGINT;
VAR
i, size: LONGINT;
BEGIN
size := 0;
IF raw THEN
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: LONGINT;
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, res: LONGINT;
BEGIN
dev.GetSize(size, res);
END UpdateCapacity;
PROCEDURE GetBufferCapacity(VAR totalCapacity, unusedCapacity: LONGINT): LONGINT;
VAR
capacity: Lib.BufferCapacity;
res: LONGINT;
BEGIN
res := Lib.ReadBufferCapacity(dev, FALSE, SYSTEM.ADR(capacity), SYSTEM.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): LONGINT;
VAR
disc: Disc;
res, i, op, nwa, secs: LONGINT;
nibble: SET;
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;
op := Disks.Write;
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*SYSTEM.SIZEOF(CueSheetEntry)) # ResOk THEN
res := dev.RequestSense(); Abort(); RETURN ErrSendingCueSheet;
END;
IF Lib.GetNextAddress(dev, nwa) # ResOk THEN RETURN ResErr END;
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;
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);
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
recStatus.operation := ClosingTrack; StatusChanged();
res := Lib.CloseTrackSess(dev, TRUE, Lib.CFTrack, Lib.TRInvisible);
WaitUntilFinished();
END;
END;
IF settings.writeType = TrackAtOnce THEN
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;
PROCEDURE VerifyCompilation(compilation: Compilation): LONGINT;
VAR
i: LONGINT;
track: InformationTrack;
BEGIN
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
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 - SYSTEM.ADR(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, SYSTEM.ADR(discBuf^), dma & UseDma) # ResOk THEN RETURN ResErr END;
r.Bytes(fileBuf^, ofs, nofBlocks*track.secsize, count);
IF ~CompareData(SYSTEM.ADR(fileBuf^) + ofs, SYSTEM.ADR(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: SYSTEM.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, res: LONGINT;
BEGIN
Unlock();
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: SYSTEM.ADDRESS): LONGINT;
VAR
res: LONGINT;
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) : LONGINT;
VAR
res: LONGINT;
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
ELSE
END;
startSec := track.startSec - track.pregap;
REPEAT
IF secsToPad >= track.secspt THEN
nofBlocks := track.secspt;
ELSE
nofBlocks := secsToPad;
END;
res := Write(op, startSec, nofBlocks, SYSTEM.ADR(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, 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, SYSTEM.ADR(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): LONGINT;
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, 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, SYSTEM.ADR(info), SYSTEM.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: SYSTEM.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, SYSTEM.SIZEOF(Lib.ATIPHeader) + SYSTEM.SIZEOF(Lib.ATIPDescriptor));
adr := SYSTEM.ADR(buf[0]);
IF Lib.ReadToc(dev, FALSE, Lib.TCFormatATIP, 0, adr, LEN(buf^)) # ResOk THEN RETURN ResErr END;
descr := SYSTEM.VAL(Lib.ATIPDescriptorPtr, adr + SYSTEM.SIZEOF(Lib.ATIPHeader));
ASSERT(SYSTEM.ADR(descr^) = SYSTEM.VAL(SYSTEM.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): LONGINT;
VAR
res: LONGINT;
page: Lib.WriteParameterPage;
BEGIN
res := Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, SYSTEM.ADR(page), SYSTEM.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: LONGINT;
BEGIN
IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, SYSTEM.ADR(page), SYSTEM.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, SYSTEM.ADR(page), SYSTEM.SIZEOF(Lib.WriteParameterPage)) # ResOk THEN
tmp := dev.RequestSense(); RETURN ResErr;
END;
RETURN ResOk;
END SetWriteParams;
PROCEDURE PrintWriteParams(): LONGINT;
VAR
page: Lib.WriteParameterPage;
tmp: LONGINT;
BEGIN
IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPWriteParameters, SYSTEM.ADR(page), SYSTEM.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: LONGINT;
BEGIN
dev.Handle(msg, res);
RETURN (msg.sense = 5) & (msg.asc = 30);
END CheckIncompatibleMedium;
PROCEDURE WaitUntilFinished*;
VAR
msg: ATADisks.GetSenseMsg;
res, tmp: LONGINT;
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;
PROCEDURE CheckNoMediumPresent*(): BOOLEAN;
VAR
msg: ATADisks.GetSenseMsg;
res: LONGINT;
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;
PROCEDURE IsInProgress*(): BOOLEAN;
VAR
msg: ATADisks.GetSenseMsg;
res: LONGINT;
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;
END GetBuffer;
PROCEDURE ReleaseBuffer;
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;
END ReleaseBuffer;
END UnbufferedReader;
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;
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;
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
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 : LONGINT;
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
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: SYSTEM.ADDRESS;
header: Lib.ModeHeader;
page: Lib.CapabilityPagePtr;
speedDescr: Lib.SpeedDescriptorPtr;
feature: Lib.MasteringFeature;
writeSpeedHeader: Lib.WriteSpeedHeader;
writeSpeedDescr: Lib.WriteSpeedDescrPtr;
BEGIN
IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPCapabilities, SYSTEM.ADR(header), SYSTEM.SIZEOF(Lib.ModeHeader)) # ResOk THEN RETURN ResErr END;
size := Utils.ConvertBE16Int(header.DataLength)+ 2;
NEW(buf, size);
adr := SYSTEM.ADR(buf[0]);
IF Lib.ModeSense(dev, Lib.MPCurrent, Lib.MPCapabilities, adr, size) # ResOk THEN RETURN ResErr END;
page := SYSTEM.VAL(Lib.CapabilityPagePtr, adr);
ASSERT(SYSTEM.ADR(page^) = SYSTEM.VAL(SYSTEM.ADDRESS, page));
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);
curSpeed := Utils.ConvertBE16Int(page.CurWriteSpeed2); DEC(curSpeed, curSpeed MOD 2*SingleSpeed);
maxSpeed := Utils.ConvertBE16Int(page.MaxWriteSpeed); DEC(maxSpeed, maxSpeed MOD 2*SingleSpeed);
INC(adr, SYSTEM.SIZEOF(Lib.CapabilityPage));
speedDescr := SYSTEM.VAL(Lib.SpeedDescriptorPtr, adr);
INC(adr, SYSTEM.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, SYSTEM.SIZEOF(Lib.SpeedDescriptor));
END;
IF Lib.GetPerformance(dev, Lib.PTypeWriteSpeed, 0H, 0H, 0, SYSTEM.ADR(writeSpeedHeader), SYSTEM.SIZEOF(Lib.WriteSpeedHeader)) # ResOk THEN
size := Utils.ConvertBE32Int(writeSpeedHeader.DataLength) + 4;
tmp := (size - SYSTEM.SIZEOF(Lib.WriteSpeedHeader)) DIV SYSTEM.SIZEOF(Lib.WriteSpeedDescr);
IF tmp > numSpeeds THEN
numSpeeds := tmp;
NEW(buf, size);
adr := SYSTEM.ADR(buf[0]);
IF Lib.GetPerformance(dev, Lib.PTypeWriteSpeed, 0H, 0H, numSpeeds, adr, size) # ResOk THEN RETURN ResErr END;
INC(adr, SYSTEM.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, SYSTEM.SIZEOF(Lib.WriteSpeedDescr));
END;
END;
END;
InsertionSort(cap.writeSpeeds^, LEN(cap.writeSpeeds));
IF Lib.GetConfiguration(dev, Lib.FOne, Lib.FMastering, SYSTEM.ADR(feature), SYSTEM.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: LONGINT;
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;
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: LONGINT;
settings: BurnSettings;
compilation: Compilation;
recorders: ARRAY MaxRecorders OF CDRecorder;
BEGIN
res := IdentifyRecorders(recorders);
IF recorders[0] # NIL THEN
KernelLog.String("Recording");
NEW(compilation);
res := compilation.AddTrack(Strings.NewString("Auto0:/Data/TRACK0.WAV"), AudioTrack, FALSE);
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~