{ jukebox1 ← Jukebox.FindJukebox[argv[1]];
IF jukebox1 =
NIL
THEN {
weOpened1 ← TRUE;
jukebox1 ← Jukebox.OpenJukebox[argv[1]];
};
jukebox2 ← Jukebox.FindJukebox[argv[3]];
IF jukebox2 =
NIL
THEN {
weOpened2 ← TRUE;
jukebox2 ← Jukebox.OpenJukebox[argv[3]];
};
tune1 ← Jukebox.OpenTune[self: jukebox1, tuneId: Convert.IntFromRope[argv[2]], write: FALSE];
tune2 ← Jukebox.OpenTune[self: jukebox2, tuneId: Convert.IntFromRope[argv[4]], write: FALSE]
{ header1: TuneAccess.ByteBlock ← TuneAccess.ReadTuneHeader[jukebox1, tune1, TuneAccess.userHeaderLength,
NIL];
header2: TuneAccess.ByteBlock ← TuneAccess.ReadTuneHeader[jukebox2, tune2, TuneAccess.userHeaderLength, NIL];
FOR i:
INT
IN [0..TuneAccess.userHeaderLength)
DO
IF header1[i] # header2[i] THEN GOTO mismatch
REPEAT
mismatch => {
cmd.out.PutF["User data headers differ at byte %d\n", IO.int[i]];
GOTO failure }
ENDLOOP;
cmd.out.PutF["User data headers match\n"]
FOR chirpNumber:
INT
IN [0..Jukebox.TuneSize[tune1])
DO
runArray1, runArray2: REF Jukebox.RunArray;
timeAccounted, samplesAccounted: Jukebox.LengthRange ← 0;
currRunElement: Jukebox.RunArrayRange ← 0;
thisLength: Jukebox.LengthRange;
skip, silence: BOOLEAN;
cmd.out.PutF["Chirp %d", IO.int[chirpNumber]];
runArray1 ← TuneAccess.ReadRunArray[jukebox1, tune1, chirpNumber, NIL ! Jukebox.MissingChirp => { runArray2 ← TuneAccess.ReadRunArray[jukebox2, tune2, chirpNumber, NIL ! Jukebox.MissingChirp => { cmd.out.PutF[" okay (missing)\n"]; LOOP }]; cmd.out.PutF[":\n chirp missing in first tune\n"]; LOOP } ];
runArray2 ← TuneAccess.ReadRunArray[jukebox1, tune1, chirpNumber, NIL ! Jukebox.MissingChirp => { cmd.out.PutF[":\n chirp missing in second tune\n"]; LOOP }];
WHILE timeAccounted<Jukebox.bytesPerChirp
DO
IF runArray1[currRunElement] # runArray2[currRunElement] THEN EXIT;
[thisLength, , skip, silence] ← TuneAccess.InterpretRunArrayElement[runArray1, currRunElement];
IF skip THEN
{ currRunElement ← currRunElement + 1;
IF runArray1[currRunElement] # runArray2[currRunElement] THEN EXIT
};
currRunElement ← currRunElement + 1;
timeAccounted ← timeAccounted + thisLength;
IF ~silence THEN samplesAccounted ← samplesAccounted + thisLength
ENDLOOP;
SELECT timeAccounted FROM
< Jukebox.bytesPerChirp =>
cmd.out.PutF[":\n Run arrays differ at element %d\n", IO.int[currRunElement]];
> Jukebox.bytesPerChirp =>
cmd.out.PutF[":\n FORMAT ERROR: Run arrays do not describe exactly %d samples\n", IO.int[Jukebox.bytesPerChirp]];
= Jukebox.bytesPerChirp =>
{ samples1: TuneAccess.ByteBlock ← TuneAccess.ReadChirpSamples[jukebox1, tune1, chirpNumber,
samplesAccounted];
samples2: TuneAccess.ByteBlock ← TuneAccess.ReadChirpSamples[jukebox2, tune2, chirpNumber,
samplesAccounted];
FOR i:
INT
IN [0..samplesAccounted)
DO
IF samples1[i] # samples2[i] THEN GOTO mismatch
REPEAT
mismatch => {
cmd.out.PutF[":\n Samples differ at element %d\n", IO.int[i]];
GOTO failure }
ENDLOOP;
cmd.out.PutF[" okay (%d samples and %d run elements)\n", IO.int[samplesAccounted], IO.int[currRunElement]]
}
ENDCASE
{ Jukebox.ArchiveCloseTune[jukebox1, tune1];
Jukebox.ArchiveCloseTune[jukebox2, tune2];
we use ArchiveCloseTune so that the read/write dates will not be altered by this command
IF weOpened1 THEN jukebox1 ← Jukebox.CloseJukebox[jukebox1];
IF weOpened2 THEN jukebox2 ← Jukebox.CloseJukebox[jukebox2]