FILE: Jukebox.mesa
Last Edited by: Swinehart, March 1, 1983 2:47 pm
Last Edited by: Stewart, December 30, 1983 11:33 am
DIRECTORY
BasicTime USING [GMT],
FS USING [nullOpenFile, OpenFile],
IO USING [STREAM],
Rope USING [ROPE],
VM USING [Interval, nullInterval];
Jukebox: DEFINITIONS =
BEGIN
Tune: TYPE = LONG POINTER TO TuneDescriptor;
pagesPerChirp: CARDINAL = 16;
bytesPerChirp: CARDINAL = 8000;
PageNumber: TYPE = INT;
ErrorCode: TYPE = {JukeboxExists, JukeboxOpen, NoJukebox, NoTunesAvailable, BadTune, JukeboxFormat, TooManyTunes, TuneLocked, Bug, TuneTooLarge, NoFreeChirps, BadTunePointer, TunesOpen, BadChirpPointer, NoDiskSpace};
Error: ERROR[reason: ErrorCode, rope: Rope.ROPE];
MissingChirp: ERROR;
EOF: ERROR;
WindowOrigin: TYPE = RECORD [file: FS.OpenFile, base: PageNumber];
CreateJukebox: PROC [name: Rope.ROPE, nPages: INT, nTunes: INT];
This procedure creates a new jukebox by the given name (which must not exist already, containing the given number of pages, and having space for nTune tune descriptors. The jukebox is NOT opened.
DeleteJukebox: PROC [name: Rope.ROPE];
Delete a jukebox, it must not be open.
FindJukebox: PROC [name: Rope.ROPE] RETURNS [Handle];
This returns the jukebox if it is already open, otherwise returns NIL.
OpenJukebox: PROC [name: Rope.ROPE] RETURNS [Handle];
This just opens a juke box and loads state into memory so that the jukebox can be used.
Info: PROC [self: Handle] RETURNS [name: Rope.ROPE, nPages, nTunes: INT];
This just returns information about the open jukebox. If the jukebox is not open, then name is NIL. Otherwise, name is the name of the open jukebox, nPages is the number of pages total in the jukebox, and nTunes is the total number of tunes it can store.
CloseJukebox: PROC [self: Handle] RETURNS [Handle];
Does the obvious thing: moves everything of interest in the jukebox back to disk. Returns NIL.
CreateTune: PROC [self: Handle, tuneId: INT ← 0] RETURNS [Tune];
Creates and opens a tune. If tuneId is negative, then we allocate a new tuneId of our choosing. Otherwise, the given tuneId is used, and if the tune already exists then it is nulled.
DeleteTune: PROC [self: Handle, tuneId: INT];
Recycles the chirp storage for the tune and marks the tune as no longer in use (this means that a later call to CreateTune[] may allocate this tune.
OpenTune: PROC [self: Handle, tuneId: INT, write: BOOL] RETURNS [Tune];
Opens a tune for reading or writing. The tune must exist.
CloseTune: PROC [self: Handle, tune: Tune];
Closes a tune and updates disk information.
TuneSize: PROC [tune: Tune] RETURNS [INT] = INLINE { RETURN[tune.size]; };
Returns size of tune in chirps.
FindChirp: PROC [self: Handle, tune: Tune, chirp: INT, signalMissingChirp: BOOLFALSE, signalEOF: BOOLFALSE] RETURNS [WindowOrigin];
Returns information that can be used to read or write a given chirp in a given open tune.
FindClientSpace: PROC [self: Handle, tuneId: INT] RETURNS [WindowOrigin];
Returns information that can be used to read or write the client space for a given tune.
Scavenge: PROC [self: Handle, stream: IO.STREAM, makeFixes: BOOL] RETURNS[nFree: INT, recovered: INT];
nFree => Free blocks after collection
recovered => Previously unknown blocks added to
free list
This procedure examines the entire jukebox (which must be open) to find chirps that are used in more than one place, for example in two different tunes or in one tune and on the free list. If makeFixes is TRUE then the inconsistencies are resolved by removing one of the pointers. Furthermore, if a bogus format is discovered in a tune header and makeFixes is TRUE, then the whole tune is deleted.
Private Definitions:
Handle: TYPE = REF JukeboxObject;
JukeboxObject: TYPE = MONITORED RECORD [
window: Jukebox.WindowOrigin ← [FS.nullOpenFile, 0],
hdr: LONG POINTER TO JukeboxHeader ← NIL,
hdrSpace: VM.Interval ← VM.nullInterval,
currentMapPageNumber: INT ← 0,
maxMapPageNumber: INT ← 0,
free: LONG POINTER TO FreeList ← NIL,
freeSpace: VM.Interval ← VM.nullInterval,
currentFreeChirpNumber: INT ← 0,
firstDesPageNumber: INT ← 0,
openTuneHeaderList: Tune ← NIL,
jukeboxName: Rope.ROPENIL
];
The structure of a jukebox file is as follows:
1) The first page contains the jukebox header, including chirp allocation and other miscellaneous information.
2) The second and following pages contain a bitmap indicating the availability of tune headers.
3) After the tune header bit map come many pages of tune headers, two pages for each possible tune. The first page of each tune header is for Bluejay use only, and the second page is for client use only (Bluejay provides ops to read it and write it). Tune headers are allocated statically, and are similar in format to Unix i-nodes (three levels of block directory).
4) All pages after the tune header are used for storage of the tunes, and for indirect tune chirp lists, and for free list blocks. The pages of a tune are grouped into "chirps". Each chirp contains several pages, in order to reduce disk access time. Each chirp contains one second of conversation in 8 pages. Chirps are page aligned, with 192 bytes per chirp thrown away to maintain alignment.
(Actually the last 192 bytes are available for storage. VoiceStream uses them to save run coded silence.)
The following are "magic" numbers placed at the beginning of all blocks to aid in detecting misuse of jukeboxes.
magicJukeboxHeader: PRIVATE INTEGER = 14513;
magicTuneBitMap: PRIVATE INTEGER = 14722;
magicTuneHeader: PRIVATE INTEGER = 28617;
magicChirpList: PRIVATE INTEGER = 7111;
magicDeepList: PRIVATE INTEGER = -4613;
magicFreeList: PRIVATE INTEGER = -15322;
magicChirp: PRIVATE INTEGER = 6215;
JukeboxHeader: PRIVATE TYPE = RECORD [
This record is read and written directly from the first page of jukebox files.
It contains overall descriptive information about the jukebox.
magic: INTEGER, -- Magic number for consistency checks.
nTunes: INT, -- Number of tune headers allocated.
nFreeChirps: INT, -- Number of free chirps.
nChirps: INT, -- Total no. of chirps in jukebox.
freeListHead: PageNumber, -- Pointer to chirp at head of free list.
firstChirp: PageNumber -- Pointer to first chirp above tune headers.
];
bitMapSize: PRIVATE INTEGER = 4;
TuneBitMap: PRIVATE TYPE = RECORD [
This record is read and written directly from the tune header bitmap pages.
One bit is contained for each tune header. One means header is available.
magic: INTEGER, -- Magic number for consistency checks.
bits: ARRAY [0..bitMapSize) OF CARDINAL
];
nHdrChirps: PRIVATE INTEGER = 10; -- Number of zeroth-level chirps in tune header.
tuneState: PUBLIC TYPE = {inUse, available};
TuneDescriptor: PUBLIC TYPE = RECORD [
Defines the structure and usage of a voice recording. This is read and written directly from the tune header pages on disk. The first definitions are kept on disk, as well as being used in main memory when the tune is "open".
magic: INTEGER, -- Magic number for consistency checks.
state: tuneState,
createDate: BasicTime.GMT,
appendDate: BasicTime.GMT,
playDate: BasicTime.GMT,
size: INT, -- Number of chirps in tune.
chirps: ARRAY [0..nHdrChirps) OF PageNumber,
Pointers to first 50 chirps of tune.
indirectChirp: PageNumber,-- Pointer to indirect chirp (each page contains
a ChirpList of chirp pointers).
deepChirp: PageNumber, -- Pointer to doubly-indirect chirp (each page
contains ChirpList of indirect chirps).
The following entries are used only in the main-memory version when the tune is open.
tuneId: INT, -- Name of the tune.
openCount: INTEGER, -- Number of times this tune is open.
next: Tune, -- All open tunes are linked together.
space: VM.Interval, -- Space used to write descriptor to disk.
writable: BOOL, -- TRUE means only one access at a time.
kill: BOOL, -- Deallocate tune as soon as it's not in use.
indirectSpace: VM.Interval,-- A space and pointer are cached (potentially)
indirectCache: LONG POINTER TO ChirpList,
indirect: INT,
deepSpace: VM.Interval, -- for one single indirect block and the double
deepCache: LONG POINTER TO ChirpList-- indirect block.
];
chirpListSize: PRIVATE INTEGER = 64;
ChirpList: PRIVATE TYPE = RECORD [
These records are read and written directly from the chirp list blocks on disk.
magic: INTEGER, -- Magic number for consistency checks.
chirps: ARRAY [0..chirpListSize) OF PageNumber,
The following values are only used when ChirpLists are in main memory.
dirty: BOOL, -- This is always FALSE on disk.
page: PageNumber -- Page from which list was read.
];
freeListSize: PRIVATE INTEGER = 16;
FreeList: PRIVATE TYPE = RECORD [
These records are read and written directly from the free list blocks on disk.
The first free pointer in the block refers to the block itself.
magic: INTEGER, -- Magic number for consistency checks.
nChirps: INTEGER, -- Number of valid chirps in the array.
next: PageNumber, -- Next chirp in free list chain.
chirps: ARRAY [0..freeListSize) OF PageNumber
];
END.
December 26, 1983 7:35 pm, Stewart, Cedar 5