DIRECTORY
FS USING [Read, Write],
IO USING [int, PutFR, STREAM],
Jukebox USING [bytesPerChirp, CloseTune, CreateTune, EOF, Error, FindChirp, MissingChirp, OpenTune, TuneSize, RunComponent, singlePktLength, RunElementType, RunArrayRange],
Process USING [Detach, Priority, priorityNormal, SetPriority],
Rope USING [ROPE],
VM USING [AddressForPageNumber],
VoiceStream;

VoiceStreamServer: MONITOR LOCKS Lock
IMPORTS FS, IO, Jukebox, Process, VM, VoiceStream
EXPORTS VoiceStream
SHARES VoiceStream =

BEGIN OPEN VoiceStream;
Foo: SIGNAL = CODE;
debugRuns: BOOL _ FALSE;
serverPriority: Process.Priority _ Process.priorityNormal;

getServerBuffer: ENTRY PROC RETURNS [handle: Handle] = {


ENABLE UNWIND => NULL;

buffer: REF Buffer;

WHILE TRUE DO
handle _ VSList;
WHILE handle # NIL DO
IF (handle.errorRope = NIL) THEN {
IF (handle.firstServerBuffer # NIL) THEN {
buffer _ handle.firstServerBuffer;
handle.firstServerBuffer _ buffer.next;
buffer.next _ handle.firstIdleBuffer;
handle.firstIdleBuffer _ buffer;
RETURN [handle];
}
ELSE
IF (handle.piece # NIL) THEN {
IF handle.firstClientBuffer = NIL THEN RETURN [handle];
IF (NOT handle.piece.flush
AND (handle.piece.nBytes > 0)
AND (handle.firstIdleBuffer # NIL))
THEN RETURN [handle]; 
};           
};
handle _ handle.next;
ENDLOOP;


BROADCAST closeCondition;
WAIT serverCondition;
ENDLOOP;
};


giveClientBuffer: ENTRY PROC [handle: Handle] = {
ENABLE UNWIND => NULL;

buffer, b2: REF Buffer;

buffer _ handle.firstIdleBuffer;
handle.firstIdleBuffer _ buffer.next;
buffer.next _ NIL;
IF handle.firstClientBuffer = NIL
THEN {
handle.firstClientBuffer _ buffer;
NOTIFY handle.newPiece;
}
ELSE {
b2 _ handle.firstClientBuffer;
WHILE b2.next # NIL DO b2 _ b2.next ENDLOOP;
b2.next _ buffer;
};
BROADCAST client;
};

setError: PUBLIC ENTRY PROC [handle: Handle, rope: Rope.ROPE, code: ErrorCode] = {


ENABLE UNWIND => NULL;
handle.errorRope _ rope;
handle.errorCode _ code;
BROADCAST client;
BROADCAST waitCondition;
};


getPiece: ENTRY PROC [handle: Handle] RETURNS [REF Piece] = {



ENABLE UNWIND => NULL;

p: REF Piece;

WHILE TRUE DO
p _ handle.piece;
IF p = NIL THEN RETURN [NIL];
IF p.flush THEN p.nBytes _ 0;
IF p.nBytes > 0 THEN RETURN [p];
IF (handle.firstClientBuffer # NIL)
OR (handle.firstServerBuffer # NIL) THEN RETURN [NIL];

IF p.tune # NIL THEN {
p _ p.next;
IF p # NIL THEN
IF p.tuneId = handle.piece.tuneId THEN {
p.tune _ handle.piece.tune;
handle.piece.tune _ NIL;
};
};
IF handle.piece.tune # NIL THEN Jukebox.CloseTune[handle.jukebox, handle.piece.tune];
handle.piece _ handle.piece.next;
IF handle.piece = NIL THEN BROADCAST client;
ENDLOOP;
RETURN [NIL];
};


server: PROC [] = {


handle: Handle;
buffer: REF Buffer;
ourSize: INT;
skipLeading: INT;
piece: REF Piece;
noChirp: BOOLEAN;

Process.SetPriority[serverPriority];

WHILE TRUE DO
ENABLE {
Jukebox.Error => {
setError[handle: handle, code: BadPiece,
rope: IO.PutFR["Piece for tune id %d starting at byte %d can't be accessed.",
IO.int[piece.tuneId], IO.int[piece.firstByte]]];
CONTINUE;
};


Jukebox.EOF => {
piece.nBytes _ 0;
CONTINUE;
};

ANY => {
setError[handle: handle, code: Bug,
rope: "Unknown error in voice stream demon."];
REJECT;
};
};
handle _ getServerBuffer[];
buffer _ handle.firstIdleBuffer;


IF buffer.toJukebox AND buffer.valid THEN {
buffer.valid _ FALSE;
buffer.window _ Jukebox.FindChirp[self: handle.jukebox, tune: handle.piece.tune,
chirp: buffer.chirp, signalMissingChirp: FALSE, signalEOF: FALSE];
IF buffer.bytesAccountedFor < Jukebox.bytesPerChirp THEN {
IF buffer.runData.runArray[buffer.runIndex].elementType ~= silence THEN
{	buffer.runIndex _ buffer.runIndex + (IF buffer.runData.runArray[buffer.runIndex].elementType = soundEnergy THEN 2 ELSE 1);
	buffer.runData.runArray[buffer.runIndex] _ [silence[0]] 
};
buffer.runData.runArray[buffer.runIndex]_ [silence[NARROW[buffer.runData.runArray[buffer.runIndex], Jukebox.RunComponent[silence]].length+(Jukebox.bytesPerChirp - buffer.bytesAccountedFor)]];
buffer.bytesAccountedFor _ Jukebox.bytesPerChirp -- just for purity
};
CheckRunData[buffer];
FS.Write[file: buffer.window.file, to: buffer.window.base, nPages: buffer.chirpSpace.count, from: VM.AddressForPageNumber[buffer.chirpSpace.page]];
};


piece _ getPiece[handle];
IF piece = NIL THEN LOOP;
buffer.keyIndex _ piece.keyIndex;


IF (piece.tune = NIL) 
THEN 
{	IF (piece.create) THEN piece.tune _ Jukebox.CreateTune[self: handle.jukebox, tuneId: piece.tuneId]
ELSE piece.tune _ Jukebox.OpenTune[self: handle.jukebox, tuneId: piece.tuneId, write: NOT piece.playback];
}
ELSE IF NOT piece.playback AND NOT piece.tune.writable THEN ERROR;


IF piece.firstByte < 0 THEN piece.firstByte _ Jukebox.TuneSize[piece.tune] * Jukebox.bytesPerChirp;

buffer.chirp _ piece.firstByte / Jukebox.bytesPerChirp;


skipLeading _ piece.firstByte MOD Jukebox.bytesPerChirp;
buffer.bytesAccountedFor _ skipLeading;
ourSize _ Jukebox.bytesPerChirp - skipLeading;
IF piece.nBytes < ourSize THEN {
IF piece.playback THEN ourSize _ piece.nBytes
ELSE piece.nBytes _ ourSize;
IF piece.playback THEN buffer.bytesAccountedFor _ Jukebox.bytesPerChirp - ourSize;
};

piece.nBytes _ piece.nBytes - ourSize;
piece.firstByte _ piece.firstByte + ourSize;


buffer.runIndex _ 0;
buffer.playedBytes _ 0;

IF (ourSize # Jukebox.bytesPerChirp) OR piece.playback THEN {
noChirp _ FALSE;
buffer.window _ Jukebox.FindChirp[self: handle.jukebox, tune: piece.tune,
chirp: buffer.chirp, signalEOF: piece.playback,
signalMissingChirp: TRUE
! Jukebox.MissingChirp => {noChirp _ TRUE; CONTINUE}];
IF noChirp THEN {
IF piece.playback THEN {
buffer.block.startIndex _ 0;
buffer.block.stopIndexPlusOne _ 0;
buffer.runData.runArray[0] _ [silence[ourSize]];
}
ELSE { -- build a fake chirp with the right structure
buffer.block.startIndex _ 0;
buffer.block.stopIndexPlusOne _ ourSize;
buffer.runData.runArray[0] _ [silence[skipLeading]];
};
}
ELSE {
FS.Read[file: buffer.window.file, from: buffer.window.base, nPages: buffer.chirpSpace.count, to: VM.AddressForPageNumber[buffer.chirpSpace.page]];
IF skipLeading > 0 THEN {
runSize: CARDINAL;
buffer.block.startIndex _ 0;
DO
WITH curr: buffer.runData.runArray[buffer.runIndex] SELECT FROM
silence => runSize _ curr.length;
singlePkt => runSize _ Jukebox.singlePktLength;
soundEnergy => runSize _ runSize _ NARROW[buffer.runData.runArray[buffer.runIndex+1], Jukebox.RunComponent[soundLength]].length;
ENDCASE => ERROR;

IF skipLeading >= runSize THEN {
skipLeading _ skipLeading - runSize;
IF buffer.runData.runArray[buffer.runIndex].elementType # silence THEN buffer.block.startIndex _ buffer.block.startIndex + runSize;
buffer.runIndex _ buffer.runIndex + (IF buffer.runData.runArray[buffer.runIndex].elementType=soundEnergy THEN 2 ELSE 1);
}
ELSE {  -- last run: 
WITH curr: buffer.runData.runArray[buffer.runIndex] SELECT FROM
silence => curr.length _ runSize - skipLeading; 
soundEnergy =>
buffer.runData.runArray[buffer.runIndex+1] _ [soundLength[IF piece.playback
THEN NARROW[buffer.runData.runArray[buffer.runIndex+1], Jukebox.RunComponent[soundLength]].length - skipLeading
ELSE skipLeading]]; -- but on record this is what is already there
singlePkt => IF piece.playback THEN buffer.playedBytes _ skipLeading
ELSE 
{buffer.runData.runArray[buffer.runIndex]  _ [soundEnergy[NARROW[buffer.runData.runArray[buffer.runIndex], Jukebox.RunComponent[soundEnergy]].energy]];
buffer.runData.runArray[buffer.runIndex+1] _ [soundLength[skipLeading]];
};
ENDCASE;
EXIT;
};
ENDLOOP;
buffer.block.stopIndexPlusOne _ buffer.block.startIndex + ourSize;
}
ELSE {
buffer.block.startIndex _ 0;
buffer.block.stopIndexPlusOne _ ourSize;
};
};
}
ELSE { -- whole chirp is involved and it is record
buffer.block.startIndex _ 0;
buffer.block.stopIndexPlusOne _ ourSize;
buffer.runData.runArray[0] _ [silence[0]]
};
buffer.toJukebox _ NOT piece.playback;
IF buffer.toJukebox THEN buffer.runData.ambientLevel _ piece.ambientLevel;
giveClientBuffer[handle];
ENDLOOP;
};


CheckRunData: PROC [buffer: REF Buffer] = {
total: CARDINAL _ 0;
run: CARDINAL;
currElement: Jukebox.RunElementType _ silence; -- must simply start as ~soundEnergy

FOR ri: Jukebox.RunArrayRange IN [0..95) DO
	IF currElement = soundEnergy -- acutally it is prevElement is this context
	THEN IF buffer.runData.runArray[ri].elementType = soundLength THEN 
	{currElement _soundLength; LOOP} ELSE ERROR;
				
currElement _ buffer.runData.runArray[ri].elementType;
WITH curr: buffer.runData.runArray[ri] SELECT FROM
silence => run _ curr.length;
singlePkt => run _ Jukebox.singlePktLength;
soundEnergy => run _ NARROW[buffer.runData.runArray[ri+1],
	Jukebox.RunComponent[soundLength]].length;
ENDCASE => ERROR;

total _ total + run;

IF total > Jukebox.bytesPerChirp THEN 
{
WITH curr: buffer.runData.runArray[ri] SELECT FROM
silence => curr.length _ run - (total - Jukebox.bytesPerChirp);
soundEnergy => buffer.runData.runArray[ri+1] _ [soundLength[run - (total - 	Jukebox.bytesPerChirp)]];
singlePkt => -- since this is last valid entry in chirp, okay to trample 'nextElement'
{	buffer.runData.runArray[buffer.runIndex]_ 		[soundEnergy[NARROW[buffer.runData.runArray[buffer.runIndex], 		Jukebox.RunComponent[singlePkt]].energy]];
	buffer.runData.runArray[ri+1] _ [soundLength[Jukebox.singlePktLength - (total - 		Jukebox.bytesPerChirp)]] 
};
ENDCASE;
IF debugRuns THEN SIGNAL Foo;
EXIT;
};

IF total = Jukebox.bytesPerChirp THEN EXIT;
ENDLOOP;
};

StartServer: PUBLIC PROC [stream: IO.STREAM] = { ioStream _ stream; };

Init: PROC = {


p: PROCESS;

p _ FORK server[];
Process.Detach[p];
};

Init[];

END.
1983, Stewart, run-encoded chirps
December 27, 1983 1:56 pm, Stewart, Cedar 5
����File: VoiceStreamServer.mesa
This file contains part of the implementation of voicestreams.  The
routines in this file implement the voicestream server, a background
process that transfers information between disk and buffers in
voicestreams.
Ades, February 10, 1986 4:01:59 pm PST
Last Edited by: Ousterhout, March 8, 1983 11:39 am compile
Last Edited by: Stewart, January 3, 1984 11:47 am
This is a monitor procedure used by the server to wait
for work to do.  For the server to have work to do, there
must not be any errors associated with the stream.  Furthermore,
either there must be a buffer waiting for the server, or there
must be an idle buffer and there must be a valid piece waiting
for the server.  If there is a server buffer waiting, this routine
transfers the frontmost server buffer to the front of the
idle list, from which position the server will process it.
Note:  it doesn't make any difference how the buffers on
the idle list are ordered.
Notify anybody trying to close a stream that we've done all
the work we can, and are waiting.
This procedure just records error information in a voice
stream and wakes up waiting clients.  This is made a separate
procedure because exclusive access is needed on the voice
stream, which the server doesn't have.
This procedure is called only by the server.
It is made a separate procedure in order to guarantee exclusive
access to the voice stream information.  Because of the high overheads
in opening and closing tunes, we check the next piece and if it's
the same tune, then don't bother closing the current one.  NIL is
returned if either there are no pieces left, or if all the space of the
frontmost piece is allocated to buffers but the buffers haven't been
processed completely.  This means we can't buffer ahead across
pieces, but the alternative results in messy synchronization
problems.
This procedure is responsible for buffering ahead and behind the
client processes.  Be VERY careful with this code.  The procedure
runs unmonitored, so that other processes can be getting and putting
voice data while we do disk operations.  However, you must be
extremely careful to make sure that the processes don't get in each
others' way.
On EOF, just discard the rest of the current piece.
IO.PutRope[ioStream, "Unknown error in voice stream demon.\n"];
If this buffer is to be written, and the buffer is valid, then write
it out.
make sure that no partial chirps are recorded!
fill the rest of the chirp with silence
last thing placed in chirp was non-silent
IO.PutF[ioStream, "Wrote chirp %d to disk.\n", IO.int[buffer.chirp]];
Collect information for the next buffer's worth of voice.  First of all,
find a piece that isn't empty.
ioStream.PutF["Number of bytes left: %d\n", IO.int[piece.nBytes]];

Set up the encryption key index (copy over from piece).
Make sure that the tune is open.
This should be changed - nobody should ever let a tune be opened down here!!!!!
Figure out which chirp this is.
Figure out which portion of the chirp we'll actually use.

Trim the tail if needed
disallow a recording that ends in the middle of a chirp
Account for the part of the piece in this chirp.
Read in the chirp, if necessary.  Note:  we don't look up the disk location until its time to actually transfer information.  This way, the jukebox routines won't allocate the space until it's actually needed.  Also, for playback we substitute a homemade all zero chirp if there isn't a chirp on the disk.
Construct a chirp with a single run of silence
Read in the whole chirp, including its runData
The value of skipLeading is the amount to chase down the runData
skip the whole run
i.e. on playback this is what is left to be played back
since we are recording it is okay to rewrite this and the next runArray entries
Not all of this is needed, since some might be silence, but we don't know how much and don't want to spend the time analyzing the runData.
IO.PutF[ioStream, "Read chirp %d from disk.\n", IO.int[buffer.chirp]];
This procedure simply forks the stream buffer server and
initializes shared state.
�Ê&��˜�Jšœ™JšœC™CJšœD™DJšœ>™>šœ
™
Icode™&—J˜�Jšœ:™:Jšœ1™1J˜�šÏk	˜	Jšœœ˜Jšœœœ˜Jšœœ(œt˜¬Jšœœ1˜>Jšœœœ˜Jšœœ˜ J˜J˜�—Jšœœœ˜%Jšœœœœ
˜1Jšœ˜šœ˜J˜�—Jšœœ
˜Jšœœœ˜Jšœœœ˜J˜:J˜�šœœœœ˜8J˜�Jšœ6™6Jšœ9™9Jšœ@™@Jšœ>™>Jšœ>™>JšœB™BJšœ9™9Jšœ:™:Jšœ8™8Jšœ™J˜�Jšœœœ˜J˜�Jšœœ˜J˜�šœœ˜
J˜šœ
œ˜šœœœ˜"šœœœ˜*J˜"J˜'J˜%J˜ Jšœ
˜Jšœ˜—š˜šœœœ˜Jšœœœœ
˜7šœœ˜Jšœ˜Jšœœ˜#Jšœœ˜—Jšœ
˜
——Jšœ˜—J˜Jšœ˜J˜�—Jšœ;™;Jšœ!™!J˜�Jš	œ˜Jšœ˜Jšœ˜—Jšœ˜J˜�—J˜�šœœœ˜1Jšœœœ˜J˜�Jšœœ˜J˜�J˜ J˜%Jšœœ˜Jšœ˜!šœ˜Jšœ"˜"Jšœ˜J˜—šœ˜J˜Jšœœœœ˜,J˜Jšœ˜—Jš	œ˜Jšœ˜J˜�—š	œ
œœœœ˜RJ˜�Jšœ8™8Jšœ=™=Jšœ9™9Jšœ&™&J˜�Jšœœœ˜J˜J˜Jš	œ˜Jš	œ˜Jšœ˜J˜�—J˜�Jš	œ
œœœœ˜=˜�Jšœ,™,J˜�Jšœ?™?JšœF™FJšœA™AJšœA™AJšœG™GJšœD™DJšœ>™>Jšœ<™<Jšœ	™	J˜�Jšœœœ˜J˜�Jšœœ˜
J˜�šœœ˜
J˜Jš
œœœœœ˜Jšœ	œ˜Jšœœœ˜ šœœ˜#Jš
œœœœœ˜6J˜�—šœ
œœ˜J˜šœœ˜šœ œ˜(J˜Jšœœ˜Jšœ˜—Jšœ˜——Jšœœœ6˜UJ˜!Jšœœœ	œ˜,Jšœ˜—Jšœœ˜
Jšœ˜J˜�—J˜�Jšœœ˜˜�Jšœ@™@JšœA™AJšœD™DJšœ=™=JšœC™CJšœ™J˜�J˜Jšœœ˜Jšœ	œ˜
Jšœ
œ˜Jšœœ˜Jšœ	œ˜J˜�J˜$J˜�šœœ˜
šœ˜˜˜(šœœE˜MJšœœ˜0——Jšœ˜	Jšœ˜J˜�—Jšœ3™3J˜�šœœ˜J˜Jšœ˜	Jšœ˜J˜�—šœ˜˜#J˜.—Jšœ=™?Jšœ˜Jšœ˜—Jšœ˜—J˜J˜ J˜�JšœD™DJšœ™J˜�šœœœ˜+Jšœœ˜˜PJšœ)œ
œ˜B—J™.šœ2œ˜:J™'JšœA˜GJ™)Jš	œ'œDœœuœ†˜ùJšœC˜CJšœ˜—Jšœ˜Jšœ`œ/˜“JšœE™EJšœ˜J˜�—JšœH™HJšœ™J˜�J˜Jšœ	œœœ˜JšœB™BJ™�Jšœ7™7Jšœ!˜!J˜�Jšœ ™ J™OJ˜�Jšœœ˜Jšœ˜šœœœL˜dJšœRœ˜jJšœ˜—šœB˜BJ˜�—Jšœ™J˜�JšœœH˜cJ˜�J˜7J˜�Jšœ9™9J˜�Jšœœ˜8Jšœ'˜'Jšœ.˜.J™�J™šœœ˜ J™7Jšœœ˜-Jšœ˜Jšœœ<˜RJ˜—J˜�J™0J˜&J˜,J˜�Jšœ±™±J˜�Jšœ˜J˜J˜�šœ#œœ˜=Jšœ
œ˜˜IJ˜/Jšœ˜Jšœ%œœ˜6—šœ	œ˜šœœ˜J™.J˜Jšœ"˜"Jšœ0˜0J˜—šœÏc.˜5J˜Jšœ(˜(Jšœ4˜4Jšœ˜—Jšœ˜—šœ˜J™.Jšœ_œ/˜’šœœ˜Jšœ	œ˜Jšœ@™@Jšœ˜š˜Jšœ0œœvœXœœ˜¥šœœ˜ J™Jšœ$˜$Jšœ@œ=˜ƒJšœ%œBœœ˜xJ˜—šœž
˜Jšœ0œ˜?Jšœzœœœd˜ûJšœ7™7Jš
œž/œ
œœ"œ˜JšœO™OJšœ:œ¤œœ˜òJ˜—Jšœ˜—JšœB˜BJ˜—šœ˜J˜J™ŠJšœ(˜(J˜—J˜—JšœF™FJšœ˜—šœž+˜2J˜Jšœ(˜(Jšœ)˜)J˜—Jšœœ˜&J˜JJ˜Jšœ˜—Jšœ˜—J˜�J˜�šÏnœœ
œ˜+Jšœœ˜Jšœœ1ž%˜cšœœ	œœž.œœœ7œœœœ˜ïJš
œ7œ#œœ`œLœœ˜Ãšœœœ#œœ´žJœ;œÈœœœœ˜ˆJšœ˜J˜—Jšœœœ˜+Jšœ˜—J˜—J˜�Jš
Ÿœœœ
œœ˜FJ˜�JšŸœœ˜˜�Jšœ8™8Jšœ™J˜�Jšœœ˜J˜�Jšœœ
˜J˜Jšœ˜—J˜�Jšœ˜J˜�Jšœ˜J˜!J˜+—�…—����%‚��A”��