DIRECTORY Commander USING [CommandProc, Register], CommandTool USING [ArgumentVector, Failed, Parse], Jukebox USING [Handle, Tune, bytesPerChirp, RunArray, FindJukebox, OpenJukebox, CloseJukebox, OpenTune, CloseTune, LengthRange, EnergyRange, RunArrayRange, RunComponent, MissingChirp, Error, singlePktLength], TuneAccess USING [ByteBlock, userHeaderLength, ReadTuneHeader, ByteSequence, ReadRunArray, ReadChirpSamples, GetCreateDate, GetWriteDate, GetReadDate], Rope USING [ROPE, Fetch, Length], IO USING [STREAM, UnsafeBlock, UnsafePutBlock, PutChar, Close], FS USING [StreamOpen, Error], Convert USING [IntFromRope], TuneArchive USING [TuneInformation, ChirpContents, TuneInformationHeader, ChirpContentsRecord, chirpContentsBytes, tuneInformationBytes, PrintArchiveInfo], BasicTime USING [Now, TimeNotKnown]; OldTunesImpl: CEDAR PROGRAM IMPORTS Commander, CommandTool, Jukebox, TuneAccess, Rope, IO, FS, Convert, BasicTime, TuneArchive = BEGIN ArchiveTune: PROC [jukeboxName: Rope.ROPE, tuneID: INT, toFile: Rope.ROPE] RETURNS [tuneInformation: TuneArchive.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.CloseTune[jukebox, tune]; IF jukebox # NIL AND weOpened THEN jukebox _ Jukebox.CloseJukebox[jukebox]; }; tuneInformation _ NEW[TuneArchive.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: TuneArchive.ChirpContents _ NEW[TuneArchive.ChirpContentsRecord]; runArray: REF Jukebox.RunArray _ NEW[Jukebox.RunArray]; unsafeOfTuneInformation: IO.UnsafeBlock _ [LOOPHOLE[tuneInformation], 0, TuneArchive.tuneInformationBytes]; byteBlockPointer: LONG POINTER _ LOOPHOLE[byteBlock]; unsafeOfByteBlock: IO.UnsafeBlock _ [byteBlockPointer + SIZE[TuneAccess.ByteSequence[0]]]; -- yetch unsafeOfChirpContents: IO.UnsafeBlock _ [LOOPHOLE[chirpContents], 0, TuneArchive.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.CloseTune[jukebox, tune]; IF weOpened THEN jukebox _ Jukebox.CloseJukebox[jukebox] }}}; BuildTuneInformation: PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, info: TuneArchive.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] }; OldRunArray: TYPE = REF ARRAY Jukebox.RunArrayRange OF CARDINAL; BuildChirpRecord: PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, chirpNumber: INT, chirpContents: TuneArchive.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; newElementsUsed: Jukebox.LengthRange; silence: BOOLEAN; oldRunArray: OldRunArray; loopHoledOldRunArray: REF Jukebox.RunArray; oldRunArrayPosition: Jukebox.RunArrayRange _ 0; loopHoledOldRunArray _ TuneAccess.ReadRunArray[jukebox, tune, chirpNumber, NIL]; TRUSTED {oldRunArray _ LOOPHOLE[loopHoledOldRunArray]}; WHILE accountedSamples RETURN }; InterpretOldRunArrayElement: PROC [oldRunArray: OldRunArray, newRunArray: REF Jukebox.RunArray, usefulArrayLength: Jukebox.RunArrayRange, oldRunArrayPosition: Jukebox.RunArrayRange] RETURNS [thisLength: Jukebox.LengthRange, newElementsUsed: Jukebox.LengthRange, silence: BOOLEAN] = { IF (oldRunArrayPosition MOD 2) = 0 THEN -- silence { IF usefulArrayLength # 0 AND newRunArray[usefulArrayLength-1].elementType = silence THEN -- last thing added to newRunArray was silence, so just add more silence { -- this works okay for case of run length = 0 TRUSTED { newRunArray[usefulArrayLength-1] _ [silence[NARROW[newRunArray[usefulArrayLength-1], Jukebox.RunComponent[silence]].length + oldRunArray[oldRunArrayPosition]]] }; -- got that ?? thisLength _ oldRunArray[oldRunArrayPosition]; newElementsUsed _ 0; silence _ TRUE } ELSE -- last thing in run wasn't silence, or this is beginning of run { IF oldRunArray[oldRunArrayPosition] = 0 THEN -- special case for run length = 0: just no change !! { thisLength _ 0; newElementsUsed _ 0; silence _ TRUE } ELSE { TRUSTED {newRunArray[usefulArrayLength] _ [silence[oldRunArray[oldRunArrayPosition]]]}; thisLength _ oldRunArray[oldRunArrayPosition]; newElementsUsed _ 1; silence _ TRUE } } } ELSE -- sound { samplesToDispose: Jukebox.LengthRange _ oldRunArray[oldRunArrayPosition]; silence _ FALSE; thisLength _ samplesToDispose; newElementsUsed _ 0; WHILE samplesToDispose>0 DO IF samplesToDispose >= Jukebox.singlePktLength THEN { TRUSTED { newRunArray[usefulArrayLength+newElementsUsed] _ [singlePkt[LAST[Jukebox.EnergyRange]]] }; samplesToDispose _ samplesToDispose - Jukebox.singlePktLength; newElementsUsed _ newElementsUsed + 1 } ELSE { TRUSTED { newRunArray[usefulArrayLength+newElementsUsed] _ [soundEnergy[LAST[Jukebox.EnergyRange]]]; newRunArray[usefulArrayLength+newElementsUsed+1] _ [soundLength[samplesToDispose]] }; samplesToDispose _ 0; newElementsUsed _ newElementsUsed + 2 } ENDLOOP } }; FrigLastElement: PROC [runArray: REF Jukebox.RunArray, usefulArrayLength: Jukebox.EnergyRange, samplesToLose: Jukebox.LengthRange] RETURNS [samplesDiscarded: BOOLEAN _ TRUE, elementAdded: BOOLEAN _ FALSE] = TRUSTED { WITH curr: runArray[usefulArrayLength] SELECT FROM silence => { curr.length _ curr.length - (samplesToLose); samplesDiscarded _ FALSE }; soundLength => curr.length _ curr.length - (samplesToLose); singlePkt => -- requires that we make a new element { runArray[usefulArrayLength] _ [soundEnergy[LAST[Jukebox.EnergyRange]]]; runArray[usefulArrayLength+1] _ [soundLength[Jukebox.singlePktLength - samplesToLose]]; elementAdded _ TRUE }; ENDCASE => ERROR }; ArchiveOldTune: Commander.CommandProc = { ENABLE {FS.Error => IF error.group = bug THEN REJECT ELSE {msg _ error.explanation; GOTO Quit}; Jukebox.Error => {msg _ rope; GOTO Quit}; BasicTime.TimeNotKnown => {msg _ "Unable to determine current time: aborting archive"; GOTO Quit} }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; GOTO Quit}]; tuneInformation: TuneArchive.TuneInformation; internalJukeboxName: Rope.ROPE; IF argv.argc # 4 THEN RETURN [$Failure, "Usage: ArchiveTune jukeboxName tuneNumber fileName"]; [tuneInformation, internalJukeboxName] _ ArchiveTune[jukeboxName: argv[1], tuneID: Convert.IntFromRope[argv[2]], toFile: argv[3]]; TuneArchive.PrintArchiveInfo[out: cmd.out, archiveFileName: argv[3], tuneInformation: tuneInformation, archivedJukeboxName: internalJukeboxName] EXITS Quit => RETURN [$Failure, msg] }; Commander.Register[key: "ArchiveOldTune", proc: ArchiveOldTune, doc: "ArchiveOldTune jukeboxName tuneNumber fileName - archive and convert a tune from an old-style [= Pre-Ades] jukebox as an FS file"]; END. ΈFile: OldTunesImpl.mesa command to archive a tune from an old style jukebox, converting it into the new standard archive format, so that it can be restored into a new style jukebox see routine BuildChirpRecord for documentation of differences in tunes Ades, March 6, 1986 3:25:19 pm PST Swinehart, February 24, 1986 1:08:21 pm PST 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 routine BuildChirpRecord distinguishes the old format from the new: the run array of an old tune was just an array [0..Jukebox.RunArrayRange] of 16 bit integers: even ones represent silence and odd ones sound. No energy or ambient values were stored, so we will convert them to max energy and min ambient noise. we need the following type to make things clear: This record is the same length and in the same position within the chirp as a new style RunArray so that we'll pick it up using TuneAccess.ReadRunArray and then LOOPHOLE it build up chirpContents, also fill runArray and byteBlock appropriately we need to be careful as zero length silences and sounds may occur in this record this gets executed if the last run element brought the total over Jukebox.bytesPerChirp, so we have to discard some Κ ˜šœ™J™œJšœ Οnœ*™FIcode™"K™+—J™šΟk ˜ Jšœ žœ˜(Jšœ žœ!˜2JšœžœΓ˜ΠJšœ žœ‡˜—Jšœžœžœ˜!Jšžœžœžœ/˜?Jšžœžœ˜Jšœžœ˜J˜›Jšœ žœ˜$J˜—Jšœžœž˜Jšžœ4žœžœ#˜dJšž˜J˜š œžœžœ žœžœžœJžœžœ˜­Jšœžœžœžœ˜Jšœžœ˜Jšœžœ˜Jšœ žœžœΟc)˜D—šœ˜šžœžœ˜š œžœ žœžœžœ˜)Jšžœžœžœ"˜4Jšžœ žœžœ žœ)˜K—Jšœ˜—J˜Jšœρ™ρJšœžœ$˜9JšœŸH˜JJšœ"žœžœ8˜yJšœ+žœ"˜PJšœ žœžœ˜7J˜Jšœ7žœ_žœN™θJšœžœžœ8˜kJšœžœžœžœ ˜5Jšœžœ#žœŸ˜cJšœžœžœ4˜eJšœžœžœ ˜8J˜Jšœ+˜+Jšžœ žœžœ˜šœ.˜.Jšœ ž˜—Jšœ˜J˜Jšœ žœ˜(J˜Jšœ>žœ˜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šœ!˜!Jšžœ žœ(˜8—˜Jšœ˜—šœžœSžœžœ˜™Jšœ`™`Jšœ4˜4Jšžœ!˜(Jšœ1˜1Jšœ/˜/Jšœ-˜-J˜#Jšžœ!˜(Jšžœ˜—J˜J˜JšœœŸ™·J™J™0Jš œ žœžœžœžœžœ˜@šœ―™―J˜—šœžœ<žœ6žœ7˜ΕJšœF™FJšžœ˜šœ!˜!Jšœ!˜!Jšœ˜Jšžœ2˜9Jšžœ˜ —J˜J™Jšœ-˜-Jšœ'˜'Jšœ*˜*J˜Jšœ ˜ Jšœ%˜%Jšœ žœ˜J˜J˜Jšœžœ˜+Jšœ/˜/J˜JšœKžœ˜PJšžœžœ˜7J˜šžœ(ž˜/Jšœ„˜„Jšžœ žœ,˜˜>Jšœ%˜%—J˜Jšžœ˜˜Jšž˜šœ˜Jšœ>žœ˜ZJšœR˜R—Jšœ˜Jšœ˜Jšœ%˜%——J˜Jšž˜—J˜—˜J˜—šœžœnžœžœžœžœžœžœ˜ΩJ™tJšžœ#žœž˜2šœ ˜ šœ/˜/Jšœž˜—Jšœ˜—Jšœ˜Jšœ.˜.šœ Ÿ&˜3šœ.žœ˜JJšœW˜WJšœžœ˜—Jšœ˜—Jšžœž˜—šœ˜J˜J˜—šΟbœ˜)Jšž˜Jš œžœ žœžœžœžœžœ˜XJ˜Jšœžœ˜)J˜JšœWžœ˜aJ˜J˜Jšœcžœ˜oJ˜Jšœ-˜-Jšœžœ˜J˜šžœžœJ˜_J˜—Jšœ‚˜‚šœ˜J˜——šž˜Jšœ ž˜—Jšœ˜J˜J˜šœ˜J˜JšœΙ˜ΙJ˜J˜—Jšžœ˜—…—&,5ξ