DIRECTORY Jukebox USING [Handle, Tune, bytesPerChirp, RunArray, FindJukebox, OpenJukebox, CloseJukebox, OpenTune, LengthRange, EnergyRange, RunArrayRange, RunComponent, MissingChirp, pagesPerChirp, ArchiveCloseTune, CreateTune, RunData, WindowOrigin, FindChirp], TuneAccess USING [ByteBlock, userHeaderLength, ReadTuneHeader, ByteSequence, ReadAmbientLevel, ReadRunArray, ReadChirpSamples, GetCreateDate, GetWriteDate, GetReadDate, WriteTuneHeader, InterpretRunArrayElement], Rope USING [ROPE, Fetch, Length, Concat, FromChar], IO USING [STREAM, UnsafeBlock, UnsafePutBlock, UnsafeGetBlock, PutChar, PutF, int, rope, Close, GetChar], FS USING [StreamOpen, FileInfo, Write], Convert USING [RopeFromTime], BasicTime USING [Now], FileNames USING [ConvertToSlashFormat], VM USING [Interval, Allocate, Free, AddressForPageNumber], TuneArchive; TuneArchiveImpl: CEDAR PROGRAM IMPORTS Jukebox, TuneAccess, Rope, IO, FS, Convert, BasicTime, FileNames, VM EXPORTS TuneArchive = BEGIN OPEN TuneArchive; ArchiveTune: PUBLIC PROC [jukeboxName: Rope.ROPE, tuneID: INT, toFile: Rope.ROPE] RETURNS [tuneInformation:TuneInformation, internalJukeboxName: Rope.ROPE] = TRUSTED { stream: IO.STREAM _ NIL; jukebox: Jukebox.Handle _ NIL; tune: Jukebox.Tune _ NIL; weOpened: BOOLEAN _ FALSE; -- all declared here for catch phrase use { ENABLE UNWIND => { IF stream # NIL THEN IO.Close[stream]; IF tune # NIL THEN Jukebox.ArchiveCloseTune[jukebox, tune]; IF jukebox # NIL AND weOpened THEN jukebox _ Jukebox.CloseJukebox[jukebox]; }; tuneInformation _ NEW[TuneInformationHeader]; { -- just so as to be able to do an assignment before all the declarations byteBlock: TuneAccess.ByteBlock _ NEW[TuneAccess.ByteSequence[MAX [TuneAccess.userHeaderLength, Jukebox.bytesPerChirp]]]; chirpContents: ChirpContents _ NEW[ChirpContentsRecord]; runArray: REF Jukebox.RunArray _ NEW[Jukebox.RunArray]; unsafeOfTuneInformation: IO.UnsafeBlock _ [LOOPHOLE[tuneInformation], 0, tuneInformationBytes]; byteBlockPointer: LONG POINTER _ LOOPHOLE[byteBlock]; unsafeOfByteBlock: IO.UnsafeBlock _ [byteBlockPointer + SIZE[TuneAccess.ByteSequence[0]]]; -- yetch unsafeOfChirpContents: IO.UnsafeBlock _ [LOOPHOLE[chirpContents], 0, chirpContentsBytes]; unsafeOfRunArray: IO.UnsafeBlock _ [LOOPHOLE[runArray]]; jukebox _ Jukebox.FindJukebox[jukeboxName]; IF jukebox = NIL THEN { jukebox _ Jukebox.OpenJukebox[jukeboxName]; weOpened _ TRUE }; stream _ FS.StreamOpen[toFile, $create]; tune _ Jukebox.OpenTune[self: jukebox, tuneId: tuneID, write: FALSE]; internalJukeboxName _ BuildTuneInformation[jukebox, tune, tuneInformation]; IO.UnsafePutBlock[stream, unsafeOfTuneInformation]; FOR i: INT IN [0..tuneInformation.jukeboxNameLength) DO IO.PutChar[stream, internalJukeboxName.Fetch[i]] ENDLOOP; byteBlock _ TuneAccess.ReadTuneHeader[jukebox, tune, TuneAccess.userHeaderLength, byteBlock]; unsafeOfByteBlock.count _ TuneAccess.userHeaderLength; IO.UnsafePutBlock[stream, unsafeOfByteBlock]; FOR j: INT IN [0..tuneInformation.sizeInChirps) DO BuildChirpRecord[jukebox, tune, j, chirpContents, runArray, byteBlock]; IO.UnsafePutBlock[stream, unsafeOfChirpContents]; unsafeOfRunArray.count _ chirpContents.runArrayLength*2; IO.UnsafePutBlock[stream, unsafeOfRunArray]; unsafeOfByteBlock.count _ chirpContents.sampleBytes; IO.UnsafePutBlock[stream, unsafeOfByteBlock] ENDLOOP; IO.Close[stream]; Jukebox.ArchiveCloseTune[jukebox, tune]; -- comments as previous use of ArchiveCloseTune IF weOpened THEN jukebox _ Jukebox.CloseJukebox[jukebox] }}}; BuildTuneInformation: PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, info: TuneInformation] RETURNS [internalJukeboxName: Rope.ROPE] = { info.jukeboxNameLength _ jukebox.jukeboxName.Length; TRUSTED {info.tuneNumber _ tune.tuneId}; info.createDate _ TuneAccess.GetCreateDate[tune]; info.writeDate _ TuneAccess.GetWriteDate[tune]; info.readDate _ TuneAccess.GetReadDate[tune]; info.archiveDate _ BasicTime.Now[]; TRUSTED {info.sizeInChirps _ tune.size}; RETURN [jukebox.jukeboxName] }; BuildChirpRecord: PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, chirpNumber: INT, chirpContents: ChirpContents, runArray: REF Jukebox.RunArray, byteBlock: TuneAccess.ByteBlock] = { ENABLE Jukebox.MissingChirp => { chirpContents.ambientLevel _ 0; chirpContents.runArrayLength _ 1; chirpContents.sampleBytes _ 0; TRUSTED {runArray[0] _ [silence[Jukebox.bytesPerChirp]]}; GOTO Return }; usefulArrayLength: Jukebox.RunArrayRange _ 0; usefulSamples: Jukebox.LengthRange _ 0; accountedSamples: Jukebox.LengthRange _ 0; thisLength: Jukebox.LengthRange; skipNextArrayElement: BOOLEAN; silence: BOOLEAN; runArray _ TuneAccess.ReadRunArray[jukebox, tune, chirpNumber, runArray]; WHILE accountedSamples RETURN }; ReadArchiveHeader: PUBLIC PROC [readableFileStream: IO.STREAM] RETURNS [tuneInformation: TuneArchive.TuneInformation, archivedJukeboxName: Rope.ROPE _ NIL] = { unsafeOfTuneInformation: IO.UnsafeBlock; unsafeBytesRead: INT; tuneInformation _ NEW[TuneInformationHeader]; unsafeOfTuneInformation _ [LOOPHOLE[tuneInformation], 0, tuneInformationBytes]; TRUSTED {unsafeBytesRead _ readableFileStream.UnsafeGetBlock[unsafeOfTuneInformation]}; IF unsafeBytesRead # tuneInformationBytes THEN ERROR; archivedJukeboxName _ NIL; FOR i: INT IN [1..tuneInformation.jukeboxNameLength] DO archivedJukeboxName _ archivedJukeboxName.Concat[Rope.FromChar[readableFileStream.GetChar]] ENDLOOP }; PrintArchiveInfo: PUBLIC PROC [out: IO.STREAM, archiveFileName: Rope.ROPE, tuneInformation: TuneArchive.TuneInformation, archivedJukeboxName: Rope.ROPE] = { length: INT; fullFName, attachedTo: Rope.ROPE; [bytes: length, fullFName: fullFName, attachedTo: attachedTo] _ FS.FileInfo[name: archiveFileName]; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF attachedTo # NIL THEN attachedTo _ FileNames.ConvertToSlashFormat[attachedTo]; out.PutF["File \"%g\":\n", IO.rope[fullFName]]; IF attachedTo # NIL THEN out.PutF["(attached to \"%g\")\n", IO.rope[attachedTo]]; out.PutF[" archive of tune number %d from jukebox \"%g\"\n", IO.int[tuneInformation.tuneNumber], IO.rope[archivedJukeboxName]]; out.PutF[" made on %g", IO.rope[Convert.RopeFromTime[tuneInformation.archiveDate]]]; IF length = -1 THEN out.PutF["\n***FS Error whilst trying to determine length of archive file\n\n"] ELSE out.PutF["; length %d bytes\n\n", IO.int[length]]; out.PutF[" Tune length %d chirps\n", IO.int[tuneInformation.sizeInChirps]]; out.PutF[" Created: %g\n", IO.rope[Convert.RopeFromTime [tuneInformation.createDate]]]; out.PutF[" Last written: %g\n", IO.rope[Convert.RopeFromTime [tuneInformation.writeDate]]]; out.PutF[" Last read: %g\n", IO.rope[Convert.RopeFromTime [tuneInformation.readDate]]] }; RestoreTune: PUBLIC PROC [fromFile: Rope.ROPE, jukeboxName: Rope.ROPE _ NIL, tuneID: INT _ -1] RETURNS [tuneIDused: INT, restoredJukeboxName: Rope.ROPE] = TRUSTED { fileStream: IO.STREAM _ NIL; jukebox: Jukebox.Handle _ NIL; tune: Jukebox.Tune _ NIL; weOpened: BOOLEAN _ FALSE; -- all declared here for catch phrase use chirpVMWindow: VM.Interval _ VM.Allocate[Jukebox.pagesPerChirp]; { ENABLE UNWIND => { IF fileStream # NIL THEN IO.Close[fileStream]; IF tune # NIL THEN Jukebox.ArchiveCloseTune[jukebox, tune]; IF jukebox # NIL AND weOpened THEN jukebox _ Jukebox.CloseJukebox[jukebox]; VM.Free[chirpVMWindow] }; tuneInformation: TuneArchive.TuneInformation; archivedJukeboxName: Rope.ROPE; byteBlock: TuneAccess.ByteBlock _ NEW[TuneAccess.ByteSequence[TuneAccess.userHeaderLength]]; byteBlockPointer: LONG POINTER _ LOOPHOLE[byteBlock]; unsafeOfByteBlock: IO.UnsafeBlock _ [byteBlockPointer + SIZE[TuneAccess.ByteSequence[0]], 0, TuneAccess.userHeaderLength]; -- yetch unsafeBytesRead: INT; fileStream _ FS.StreamOpen[fromFile]; [tuneInformation, archivedJukeboxName] _ ReadArchiveHeader[readableFileStream: fileStream]; IF jukeboxName = NIL THEN jukeboxName _ archivedJukeboxName; IF tuneID = -1 THEN tuneID _ tuneInformation.tuneNumber; IF tuneID < -1 THEN tuneID _ -1; -- means "create a tune from unallocated ID space" jukebox _ Jukebox.FindJukebox[jukeboxName]; IF jukebox = NIL THEN { jukebox _ Jukebox.OpenJukebox[jukeboxName]; weOpened _ TRUE }; tune _ Jukebox.CreateTune[self: jukebox, tuneId: tuneID]; RestoreTuneInformation[tune, tuneInformation]; tuneIDused _ tune.tuneId; restoredJukeboxName _ jukebox.jukeboxName; unsafeBytesRead _ fileStream.UnsafeGetBlock[unsafeOfByteBlock]; IF unsafeBytesRead # TuneAccess.userHeaderLength THEN ERROR; TuneAccess.WriteTuneHeader[jukebox, tune, byteBlock]; FOR j: INT IN [0..tuneInformation.sizeInChirps) DO RestoreChirp[jukebox, tune, j, fileStream, chirpVMWindow] ENDLOOP; Jukebox.ArchiveCloseTune[jukebox, tune]; -- special one; enables the restored timestamp information to persist IF weOpened THEN jukebox _ Jukebox.CloseJukebox[jukebox]; IO.Close[fileStream]; VM.Free[chirpVMWindow] }}; RestoreTuneInformation: PROC [tune: Jukebox.Tune, tuneInformation: TuneInformation] = TRUSTED { tune.createDate _ tuneInformation.createDate; tune.appendDate _ tuneInformation.writeDate; tune.playDate _ tuneInformation.readDate }; RestoreChirp: PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, chirpNumber: INT, fileStream: IO.STREAM, chirpVMWindow: VM.Interval] = TRUSTED { chirpContents: ChirpContents _ NEW[ChirpContentsRecord]; unsafeOfChirpContents: IO.UnsafeBlock _ [LOOPHOLE[chirpContents], 0, chirpContentsBytes]; unsafeBytesRead: INT _ fileStream.UnsafeGetBlock[unsafeOfChirpContents]; chirpVMWindowpointer: LONG POINTER _ LOOPHOLE[VM.AddressForPageNumber[chirpVMWindow.page]]; unsafeOfSamples: IO.UnsafeBlock _ [chirpVMWindowpointer, 0, chirpContents.sampleBytes]; unsafeOfRunData: IO.UnsafeBlock _ [chirpVMWindowpointer + (Jukebox.bytesPerChirp/2), 0, chirpContents.runArrayLength*2]; runData: Jukebox.RunData _ LOOPHOLE[unsafeOfRunData.base]; IF unsafeBytesRead # chirpContentsBytes THEN ERROR; unsafeBytesRead _ fileStream.UnsafeGetBlock[unsafeOfRunData]; IF unsafeBytesRead # chirpContents.runArrayLength*2 THEN ERROR; unsafeBytesRead _ fileStream.UnsafeGetBlock[unsafeOfSamples]; IF unsafeBytesRead # chirpContents.sampleBytes THEN ERROR; runData.ambientLevel _ chirpContents.ambientLevel; IF chirpContents^ = [0, 1, 0] AND runData.runArray[0] = [silence[Jukebox.bytesPerChirp]] THEN RETURN; -- this will have the effect of leaving a 'missing chirp' { chirpWindow: Jukebox.WindowOrigin _ Jukebox.FindChirp[self: jukebox, tune: tune, chirp: chirpNumber, signalMissingChirp: FALSE, signalEOF: FALSE]; FS.Write[file: chirpWindow.file, to: chirpWindow.base, nPages: Jukebox.pagesPerChirp, from: VM.AddressForPageNumber[chirpVMWindow.page]] } }; END. . FILE: TuneArchiveImpl.mesa Ades, February 20, 1986 4:02:25 pm PST we use ArchiveCloseTune here so that the read/write dates will not be altered by the archive these variables and the RETURN parameters are used to retrieve data from the jukebox: internalJukeboxName is the name for the jukebox that is actually stored in the jukebox when open, as opposed to the parameter passed to Jukebox.OpenJukebox these are the arguments corresponding to the above for IO.UnsafePutBlock: in cases where it is not given above (thus defaulting to 0), before a call to IO.UnsafePutBlock the block's count field should be set appropriately (in bytes) build up the tuneInformation, mainly by reading the tune and return the jukebox name as a string build up chirpContents, also fill runArray and byteBlock appropriately now have enough info to set return parameters the only information that need to be copied back into the [non-user portion of the] tune header are the three dates: the rest of the TuneInformation is either discarded, overriden by the parameters to RestoreTune or written into the tune explicitly by various parts of RestoreTune there isn't even the need to write this information through, since this will be done by Jukebox.ArchiveCloseTune N.B. the preceding lines, as well as being horrible, assume knowledge of the exact structure of a chirp. They are also not very efficient as they could be done once only outside RestoreChirp, but this is for the sake of abstraction otherwise, if there is useful data then allocate another chirp in the tune and then write into it Κ %˜J™šœ™Icode™&—J™šΟk ˜ Jšœœο˜όJšœ œΔ˜ΤJšœœœ#˜3JšœœœY˜iJšœœ˜'Jšœœ˜Jšœ œ˜Jšœ œ˜'Jšœœ4˜œ˜EJ˜JšœK˜KJšœ1˜3J˜Jš œœœ(œœ/œ˜qJ˜Jšœ]˜]Jšœ6˜6Jšœ+˜-J˜šœœœ#˜2JšœG˜GJšœ/˜1Jšœ8˜8Jšœ*˜,Jšœ4˜4Jšœ*˜,—Jšœ˜J˜Jšœ˜JšœX˜XJšœ œ(˜8—˜Jšœ˜—šžœœGœœ˜Jšœ`™`Jšœ4˜4Jšœ!˜(Jšœ1˜1Jšœ/˜/Jšœ-˜-J˜#Jšœ!˜(Jšœ˜—J˜J˜šžœœ<œ*œ7˜ΉJšœF™FJšœ˜šœ!˜!Jšœ!˜!Jšœ˜Jšœ2˜9Jšœ˜ —J˜J™Jšœ-˜-Jšœ'˜'Jšœ*˜*J˜Jšœ ˜ Jšœœ˜Jšœ œ˜J˜JšœI˜Išœ(˜/Jšœq˜qJšœ œ,˜