DIRECTORY Jukebox USING [Handle, WindowOrigin, Error, Tune, Info, FindClientSpace, magicTuneHeader, RunArrayRange, RunComponent, bytesPerChirp, pagesPerChirp, FindChirp, singlePktLength, MissingChirp, LengthRange, EnergyRange, RunData, EOF, RunArray, bytesPerMS], VM USING [Allocate, Interval, AddressForPageNumber, Free], File USING [wordsPerPage], FS USING [Read, Write], PrincOps USING [ByteBltBlock], PrincOpsUtils USING [ByteBlt], TuneAccess; TuneAccessImpl: CEDAR PROGRAM IMPORTS Jukebox, VM, FS, PrincOpsUtils EXPORTS TuneAccess SHARES Jukebox = BEGIN OPEN TuneAccess; NextTuneNumber: PUBLIC PROC [jukebox: Jukebox.Handle, currentTuneID: INT _ -1] RETURNS [tuneID: INT] = TRUSTED { headerWindow: Jukebox.WindowOrigin; space: VM.Interval _ VM.Allocate[count: 1]; tuneFirstDiskHeaderPage: Jukebox.Tune _ LOOPHOLE[VM.AddressForPageNumber[page: space.page]];-- name is a reminder only to use those parts which reside on disk and no more than the first page thereof tuneFound: BOOLEAN _ FALSE; tuneHWM: INT; IF currentTuneID < -1 THEN currentTuneID _ -1; IF jukebox.hdr = NIL THEN ERROR Jukebox.Error[reason: NoJukebox, rope: "Jukebox not open"]; [tuneHWM: tuneHWM] _ Jukebox.Info[jukebox]; DO currentTuneID _ currentTuneID + 1; IF currentTuneID > tuneHWM THEN EXIT; headerWindow.file _ jukebox.window.file; headerWindow.base _ jukebox.firstDesPageNumber + 2*currentTuneID; FS.Read[file: headerWindow.file, from: headerWindow.base, nPages: space.count, to: VM.AddressForPageNumber[space.page]]; tuneFound _ tuneFirstDiskHeaderPage.state = inUse; IF tuneFound THEN EXIT ENDLOOP; VM.Free[space]; RETURN [IF tuneFound THEN currentTuneID ELSE -1] }; ReadTuneHeader: PUBLIC PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, nBytes: CARDINAL _ userHeaderLength, block: ByteBlock _ NIL] RETURNS [ByteBlock] = TRUSTED { headerFileWindow: Jukebox.WindowOrigin; headerVMWindow: VM.Interval; byteBltBlockOfVMWindow: PrincOps.ByteBltBlock; pointerToSequence: LONG POINTER; byteBltBlockOfSequence: PrincOps.ByteBltBlock; bytesBlted: INT; IF nBytes>userHeaderLength THEN nBytes _ userHeaderLength; IF jukebox.hdr = NIL THEN ERROR Jukebox.Error[reason: NoJukebox, rope: "Jukebox not open"]; IF tune.magic # Jukebox.magicTuneHeader THEN ERROR Jukebox.Error[BadTunePointer, "Corrupt tune or tune pointer"]; IF block = NIL OR block.max { length _ currEl.length; energy _ 0; silence _ TRUE }; singlePkt => { length _ Jukebox.singlePktLength; energy _ currEl.energy }; soundEnergy => { energy _ currEl.energy; length _ NARROW[runArray[currArrayIndex+1], Jukebox.RunComponent[soundLength]].length; skipNextArrayElement _ TRUE }; ENDCASE => ERROR; }; GetEnergyProfile: PUBLIC PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, chirpNumber: INT, energyBlock: EnergyBlock _ NIL, divisions: INT _ Jukebox.bytesPerChirp/Jukebox.singlePktLength, signalMissingChirp: BOOLEAN _ FALSE] RETURNS [EnergyBlock] = { ENABLE Jukebox.MissingChirp => { IF signalMissingChirp THEN REJECT ELSE { FOR i: NAT IN [0..energyBlock.length) DO energyBlock[i] _ 0 ENDLOOP; GOTO ReturnEnergyBlock } }; runArray: REF ARRAY Jukebox.RunArrayRange OF Jukebox.RunComponent; endOfLastDivision: Jukebox.LengthRange; divisionSize: INT; currDivision: INT _ 0; endOfCurrDivision: Jukebox.LengthRange; endOfCurrRunElement: Jukebox.LengthRange _ 0; energyOfCurrRunElement: Jukebox.EnergyRange; currRunArrayIndex: Jukebox.RunArrayRange _ 0; samplesAccountedFor: Jukebox.LengthRange _ 0; samplesBeingAccounted: Jukebox.LengthRange; energyAccumulator: INT _ 0; NextRunElement: PROC RETURNS [energyOfCurrRunElement: Jukebox.EnergyRange] = INLINE { length: Jukebox.LengthRange; dualElementRepresentation: BOOLEAN; [length: length, energy: energyOfCurrRunElement, skipNextArrayElement: dualElementRepresentation] _ InterpretRunArrayElement[runArray, currRunArrayIndex]; currRunArrayIndex _ currRunArrayIndex + (IF dualElementRepresentation THEN 2 ELSE 1); endOfCurrRunElement _ endOfCurrRunElement + length }; IF divisions < 1 OR divisions>Jukebox.bytesPerChirp THEN ERROR; divisionSize _ Jukebox.bytesPerChirp/divisions; endOfLastDivision _ divisionSize*divisions; endOfCurrDivision _ divisionSize; IF energyBlock = NIL OR energyBlock.max= endOfCurrRunElement THEN energyOfCurrRunElement _ NextRunElement[]; samplesBeingAccounted _ MIN[endOfCurrRunElement, endOfCurrDivision] - samplesAccountedFor; energyAccumulator _ energyAccumulator + INT[samplesBeingAccounted]*INT[energyOfCurrRunElement]; samplesAccountedFor _ samplesAccountedFor + samplesBeingAccounted ENDLOOP; energyBlock[currDivision] _ energyAccumulator/INT[divisionSize]; energyAccumulator _ 0; currDivision _ currDivision + 1; IF endOfCurrDivision = Jukebox.bytesPerChirp THEN EXIT ELSE endOfCurrDivision _ endOfCurrDivision + divisionSize ENDLOOP; RETURN[energyBlock]; EXITS -- for handling MissingChirp error ReturnEnergyBlock => RETURN[energyBlock] }; ReadChirpSamples: PUBLIC PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, chirpNumber: INT, nBytes: CARDINAL _ Jukebox.bytesPerChirp, block: ByteBlock _ NIL] RETURNS [ByteBlock] = TRUSTED { sampleFileWindow: Jukebox.WindowOrigin; sampleVMWindow: VM.Interval; byteBltBlockOfVMWindow: PrincOps.ByteBltBlock; pointerToSequence: LONG POINTER; byteBltBlockOfSequence: PrincOps.ByteBltBlock; bytesBlted: INT; filePagesToRead: INT; IF nBytes>Jukebox.bytesPerChirp THEN nBytes _ Jukebox.bytesPerChirp; filePagesToRead _ (nBytes+(File.wordsPerPage*2)-1)/(File.wordsPerPage*2); IF block = NIL OR block.max { IF signalMissingChirp THEN REJECT ELSE { sampleNumber, silenceNumber: INT _ 0; WHILE sampleNumber < Jukebox.bytesPerChirp DO block[sampleNumber] _ encryptedSilence[silenceNumber]; sampleNumber _ sampleNumber + 1; silenceNumber _ (silenceNumber + 1) MOD Jukebox.bytesPerMS ENDLOOP; GOTO Quit } }; runArray: REF Jukebox.RunArray; chirpFormat: ChirpFormat _ NEW[ChirpFormatRecord]; samplesAccounted: Jukebox.LengthRange _ 0; newLength: Jukebox.LengthRange; skipNextArrayElement, currentlySilence: BOOLEAN; currArrayIndex: Jukebox.RunArrayRange _ 0; validSamples: Jukebox.LengthRange; IF block = NIL OR block.max { block.length _ Jukebox.bytesPerChirp; RETURN[block] } }; WimpFakeReverseByteBlt: PROC [block: ByteBlock, from: Jukebox.LengthRange, to: Jukebox.LengthRange, nBytes: Jukebox.LengthRange] = INLINE { destCounter: INT _ to + nBytes - 1; FOR sourceCounter: INT DECREASING IN [from..from+nBytes) DO block[destCounter] _ block[sourceCounter]; destCounter _ destCounter - 1 ENDLOOP }; ReadAmbientLevel: PUBLIC PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, chirpNumber: INT] RETURNS [ambientLevel: Jukebox.EnergyRange] = TRUSTED { runDataFileWindow: Jukebox.WindowOrigin; runDataVMWindow: VM.Interval; runData: Jukebox.RunData; pageOffsetInChirp: INT = Jukebox.bytesPerChirp/(File.wordsPerPage*2); byteOffsetInPage: INT = Jukebox.bytesPerChirp MOD (File.wordsPerPage*2); runDataPages: INT = Jukebox.pagesPerChirp - pageOffsetInChirp; runDataFileWindow _ Jukebox.FindChirp[self: jukebox, tune: tune, chirp: chirpNumber, signalMissingChirp: TRUE, signalEOF: TRUE]; runDataFileWindow.base _ runDataFileWindow.base + pageOffsetInChirp; runDataVMWindow _ VM.Allocate[count: 1]; runData _ VM.AddressForPageNumber[runDataVMWindow.page] + (byteOffsetInPage/2); FS.Read[file: runDataFileWindow.file, from: runDataFileWindow.base, nPages: runDataPages, to: VM.AddressForPageNumber[runDataVMWindow.page]]; ambientLevel _ runData.ambientLevel; VM.Free[runDataVMWindow] }; WriteAmbientLevel: PUBLIC PROC [jukebox: Jukebox.Handle, tune: Jukebox.Tune, ambientLevel: Jukebox.EnergyRange, fromChirp: INT, toChirp: INT] = TRUSTED { runDataFileWindow: Jukebox.WindowOrigin; runDataVMWindow: VM.Interval; runData: Jukebox.RunData; pageOffsetInChirp: INT = Jukebox.bytesPerChirp/(File.wordsPerPage*2); byteOffsetInPage: INT = Jukebox.bytesPerChirp MOD (File.wordsPerPage*2); runDataPages: INT = Jukebox.pagesPerChirp - pageOffsetInChirp; IF fromChirp < 0 THEN ERROR; IF toChirp = -1 THEN toChirp _ tune.size - 1; IF toChirp < fromChirp THEN toChirp _ fromChirp; IF toChirp >= tune.size THEN ERROR Jukebox.EOF[]; runDataVMWindow _ VM.Allocate[count: 1]; runData _ VM.AddressForPageNumber[runDataVMWindow.page] + (byteOffsetInPage/2); FOR chirp: INT IN [fromChirp..toChirp] DO ENABLE Jukebox.MissingChirp => LOOP; runDataFileWindow _ Jukebox.FindChirp[self: jukebox, tune: tune, chirp: chirp, signalMissingChirp: TRUE, signalEOF: TRUE]; runDataFileWindow.base _ runDataFileWindow.base + pageOffsetInChirp; FS.Read[file: runDataFileWindow.file, from: runDataFileWindow.base, nPages: runDataPages, to: VM.AddressForPageNumber[runDataVMWindow.page]]; runData.ambientLevel _ ambientLevel; FS.Write[file: runDataFileWindow.file, to: runDataFileWindow.base, nPages: runDataPages, from: VM.AddressForPageNumber[runDataVMWindow.page]] ENDLOOP; VM.Free[runDataVMWindow] }; END. dFILE: TuneAccessImpl.mesa Ades, February 27, 1986 6:17:12 pm PST This really ought not to be fully implemented in here: it ought to call a procedure FindHeaderSpace analogous to FindClientSpace in Jukebox. But FindClientSpace is not an entry procedure, so why bother? after all the statutory checks, do we need to allocate space for the return block? these constants make it clearer whence to pick up the run length data each runArray element is machine dependent and two bytes long no need to check if jukebox and tune specifications are okay, since FindChirp must do so work out how many pages are needed to get all the data from disk -- it starts on the first byte of the chirp's first page no need to check if jukebox and tune specifications are okay, since FindChirp must do so internal working record used by ReadFormattedSamples for restructuring the samples in a chirp count of how many of the following are actually in use + 1 for efficiency this routine works out how the chirp divides into sound and silence, reads the samples straight from the jukebox into the return array and then ByteBlts maximally sized blocks, ingoring any lumps already in correct place. This is efficient in that the chirps usually have very few silence/sound transitions and many are 100% sound. Alas this needs a reverse byteblt operation, which is not implemented on Dorados although part of PrincOps. Since most chirps are entirely sound, writing a fake reverse byte blt isn't as bad a solution as it seems! first allocate any blocks required, since ReadRunArray may well produce a signal of MissingChirp now read the run array and from it build the ChirpFormat record we now know amongst other things how many samples are stored on disk: read them and then use the chirpFormat to reposition them: it goes without saying that we need to work backwards through the chirpFormat list to avoid overwriting things incorrectly! these constants make it clearer whence to pick up the RunDataObject - which ends in the ambient level (a single 16 bit field) the code below picks up the entire RunDataObject, even if it means reading more than one page to do so. This is so that the code wouldn't stop working if the run data were to stretch across more than one page, but does not impede efficiency since this is only a very remote possibility [a major implementation change] no need to check if jukebox and tune specifications are okay, since FindChirp must do so the above line seems to blow up if byteOffsetInpage is odd: however I'm not going to test for it because (i) the entire jukebox blows up (ii) it would do so in the same way and this line would effectively still be correct (iii) no clown is going to make bytesPerChirp odd anyhow all the comments from ReadAmbientlevel apply here too error signalled here to avoid half the chirps becoming updated before it is signalled by Jukebox.FindChirp Κ˜šœ™J™&—Jšœ™šΟk ˜ JšœœΥœ˜ώJšœœ2˜:Jšœœ˜Jšœœ˜Jšœ œ˜Jšœœ ˜Jšœ ˜ —Jšœœœœ œœœ œ ˜hJšœœ ˜J˜šΟnœœœ*œœ œœ˜pJ™ΚJ™Jšœ#˜#Jšœœ œ˜+Jšœ(œœ)Οcj˜ΖJšœ œœ˜Jšœ œ˜ J˜Jšœœ˜.Jšœœœœ<˜[Jšœ+˜+J˜šœ˜Jšœ#˜#Jšœœœ˜%J˜(JšœA˜AJšœQœ#˜xJšœ2˜2Jšœ œ˜—šœ˜J˜—Jšœ ˜Jšœœ œœ˜0—J˜J˜šžœœœ7œ(œœœ˜¦Jšœ'˜'Jšœœ ˜Jšœ.˜.Jšœœœ˜ Jšœ.˜.Jšœ œ˜J˜Jšœœ˜:Jšœœœœ<˜[Jšœ&œœ?˜qJ™J™RJš œ œœœ œ˜JJšœŸ˜*J˜JšœA˜AJšœœ˜'Jšœœœ9˜^Jšœœ Ÿ0˜UJšœ(œŸ˜[Jšœ9Ÿ˜RJ˜JšœOœ,˜Jšœ]˜]Jšœ˜J˜Jšœœœœ˜=Jšœ˜—J˜J™š žœœœIœœ˜Jšœ'˜'Jšœœ ˜Jšœ.˜.Jšœœœ˜ Jšœ.˜.Jšœ œ˜J˜Jšœ œ*˜6Jšœœœœ<˜[Jšœ&œœ?˜qJ™JšœA˜AJšœœ˜'Jšœœœ9˜^Jšœœ Ÿ0˜UJšœ(œŸ˜[Jšœ9Ÿ˜RJ˜JšœOœ,˜Jšœ]˜]Jšœ˜Jšœœœ˜%Jš˜šœ˜JšœPœ,˜€Jšœ˜—J˜—˜J™—šž œœœ<œ œœœœœ˜ͺJšœ)˜)Jšœœ ˜Jšœ.˜.Jšœ,˜,Jšœ œ˜J˜J™EJšœœ0˜FJšœœœ˜IJšœœ-˜>Jšœœœœ˜XJ™=J˜Jš œ œœ œœœ˜[J™J™XJšœjœ œ˜JšœF˜FJ˜Jšœœ˜4Jšœœœd˜‰Jšœœ˜>—˜Jšœ^œ.˜Jšœ[˜[Jšœ˜—˜Jšœœœ˜)Jšœ ˜—J˜šžœœœ œ:œRœœ œœ˜φšœœ"œ˜2šœ ˜ šœ˜Jšœ ˜ Jšœ ˜—J˜—˜ šœ#˜#Jšœ˜—J˜—˜šœ˜Jšœ œG˜VJšœ˜—J˜—Jšœœ˜—J˜—šžœœœ<œœ œFœœœ˜όJšœ˜šœœœœ˜(š œœœœœœ˜GJšœ˜—J˜—J˜—˜Jšœ œœœ˜BJ˜'Jšœœ˜Jšœœ˜J˜'J˜-J˜,J˜-J˜-J˜+Jšœœ˜J˜šžœœœ9˜UJšœ˜Jšœœ˜#Jšœ›˜›Jšœ)œœœ˜UJ˜2—˜J˜—Jš œœœ!œœ˜?J˜/J˜+J˜"Jš œœœœœ˜dJšœ˜J˜JšœZœ˜_J˜šœ(˜/šœ)˜0Jšœ-œ+˜^Jšœœ?˜ZJ˜_J˜A—Jšœ˜J˜J˜@J˜J˜ J˜p—Jšœ˜J˜Jšœ˜Jšœ˜JšœŸ"˜(Jšœœ ˜(—J˜J˜šžœœœ<œ œ-œœœ˜ΏJšœ'˜'Jšœœ ˜Jšœ.˜.Jšœœœ˜ Jšœ.˜.Jšœ œ˜Jšœœ˜J˜J™yJšœœ ˜DJšœI˜IJ˜Jš œ œœœ œ˜JJšœŸ˜*J˜J™XJšœhœ œ˜J˜Jšœœ"˜5Jšœœœ9˜^Jšœœ Ÿ0˜UJšœ(œŸ˜[Jšœ9Ÿ˜RJ˜Jšœ]œ,˜Jšœ]˜]Jšœ˜J˜Jšœœœœ˜=Jšœ˜—Jšœ˜J˜Jšœ^™^Jšœ œœ˜*J˜Jšœœ ˜ ˜(J™:Jšœœ ˜,šœ Ÿ˜6Jšœ!Ÿ"˜CJšœ Ÿ2˜RJšœ ˜—J˜—J˜J˜šžœ œ<œœ'œœœœ˜ΰJšœ˜šœœœœ˜(šœ œ˜(šœ&˜-Jšœ6˜6Jšœ ˜ Jšœ$œ˜:—Jšœ˜Jšœ˜ —J˜—J˜J˜Jšœ œ˜Jšœœ˜2J˜*J˜Jšœ(œ˜0Jšœ*˜*J˜"J˜J™±J™Jšœ`™`Jš œ œœ!œ œ&˜hJš œœœœœ˜VJ˜J™?Jšœ4œ˜9J˜Jšœ!Ÿ.˜OJ˜$J˜"Jšœœ˜%šœ*˜1Jšœ˜Jšœ0˜0Jšœ#œœœ˜JJ˜JšœIœ˜P˜=J˜8Jšœwœœ7œ˜βJ˜¨JšœG˜G—J˜J˜Jšœu˜u—Jšœ˜ Jšœό™όJ™JšœJœ7œ5œ˜ΔJšœJ˜JJ™šœ œœ ˜MJšœ˜ JšœŸA˜FšœD˜DJšœQ˜QJšœœ˜šœ˜%Jšœ4˜4Jšœ˜Jšœ$œ˜:—Jšœ˜JšœœœŸ0˜Q—J˜Jš˜š œœœœœ˜:JšœEœ$œ—˜ˆ—J˜—Jšœ˜J˜J˜%Jšœ˜ —Jš˜šœ˜šœ(˜(Jšœ˜ —J˜—Jšœ˜J™šžœœgœ˜‹Jšœ œ˜#šœ8˜;Jšœ*˜*Jšœ˜—J˜—J˜J™š žœœœ<œœ'œ˜•Jšœ(˜(Jšœœ ˜Jšœ˜J˜J™}J™ΎJšœœ/˜EJšœœœ˜HJšœœ-˜>J˜J™XJšœiœ œ˜€JšœD˜DJ˜Jšœœ˜(Jšœ œC˜OJšœ–™–J˜Jšœ\œ-˜J˜Jšœ$˜$Jšœ˜—˜J™—š žœœœ]œ œœ˜™Jšœ5™5Jšœ(˜(Jšœœ ˜Jšœ˜J˜Jšœœ/˜EJšœœœ˜HJšœœ-˜>J˜Jšœœœ˜Jšœœ˜-Jšœœ˜0Jšœœœ œ˜2J™jJ˜Jšœœ˜(Jšœ œC˜OJ™Jšœœœ˜)˜J˜J˜Jšœcœ œ˜zJšœD˜D—˜Jšœ\œ-˜J˜$Jšœ]œ,˜—Jšœ˜J˜Jšœ˜—˜J™—J™Jšœ˜˜Icode™—K™—…—Eϊ`n