File: VoiceStream.mesa
This file contains routines that provide a stream-like facility
for moving voice data between the jukebox and the Ethernet.
Last Edited by: Ousterhout, March 8, 1983 11:34 am
Last Edited by: L. Stewart, December 30, 1983 11:30 am
Last Edited by: Swinehart, September 4, 1984 1:30:32 pm PDT
Ades, February 6, 1986 4:24:23 pm PST
DIRECTORY
IntervalTimer USING [Microseconds],
IntervalTimer USING [Pulses],
IO USING [STREAM],
Jukebox USING [Handle, RunData, RunDataObject, Tune, WindowOrigin, RunArrayRange, EnergyRange],
PrincOps USING [ByteBltBlock],
PupDefs USING [PupSocket],
Rope USING [ROPE],
VM USING [Interval];
VoiceStream: DEFINITIONS =
BEGIN
ErrorCode: TYPE = {Bug, BadPiece, StreamClosed, Timeout, Hungup};
The signals have the following meaning:
Bug: internal error in this module.
BadPiece: the piece referred to a non-existent tune or area within tune.
StreamClosed: operations were attempted on a stream after closing it.
Timeout: the Etherphone stopped responding.
Hungup: the Etherphone hungup.
Error: ERROR [reason: ErrorCode, rope: Rope.ROPE];
The following byte count is used to refer to an entire tune. It's just a
very large integer.
wholeTune: INT;
Open: PROC [jukebox: Jukebox.Handle, proc: NotifyProc ← NIL, clientData: REF ANY ← NIL] RETURNS [handle: Handle];
This sets up a voice stream, which is a connection between the jukebox and the network. The stream is bidirectional. However, this call will not start information flowing. In order for information transfer to occur, two more things must happen: a) a socket to provide or receive the information must be identified with a call to SetSocket; b) one or more areas of the jukebox must be identified with calls to AddPiece. If the pieces run out, then the socket routine will just wait. If proc is specified, then it is invoked when pieces run out (this routine may also be invoked occasionally when pieces haven't really run out: call Empty to find out for sure).
Close: PROC [handle: Handle];
This flushes all the information in the stream, and kills a socket connection if there was one.
SetSocket: PROC [handle: Handle, socket: PupDefs.PupSocket];
This procedure sets up a background procedure that will pass data between the voice stream and the given socket. If there was already a socket associated with the stream, the old association is broken. If the new socket is NIL, then the stream is left with no socket association. Pieces in the stream are not flushed when the socket association is broken.
AddPiece: PROC [handle: Handle, tuneId: INT, firstByte: INT, nBytes: INT, create: BOOLEAN ← FALSE, playback: BOOLEAN ← TRUE, keyIndex: NAT ← 0, flush: BOOLEAN ← FALSE, ambientLevel: Jukebox.EnergyRange ← 0];
This call indicates an area of the jukebox to be read or written, and adds that area to the list pending for the stream (if flush is TRUE, then any pending information is flushed). If firstByte is less than zero, then the piece starts at the end of the tune. Create specifies whether or not the tune should be nulled if it already exists. Playback specifies the direction of information flow: if TRUE, then information is played from the jukebox; if FALSE, information is recorded into the jukebox.
FlushPieces: PROC [handle: Handle];
This procedure throws away all of the pieces pending for the stream.
IsEmpty: PROC [handle: Handle] RETURNS[BOOLEAN];
Indicates whether or not there are any pieces pending for the stream.
WaitEmpty: PROC [handle: Handle];
This procedure doesn't return until the stream is empty.
Get: PROC [handle: Handle, maxSilentBytes: NAT, block: PrincOps.ByteBltBlock, wait: BOOL ← FALSE] RETURNS[silence: NAT, bytesTransferred: NAT, keyIndex: NAT];
Reads information from a stream into a given area. Not normally invoked from outside this package. Returns value smaller than block size, or [0, 0], when pieces run out or if the current piece is for recording.
Put: PROC [handle: Handle, silentBytes: NAT, block: PrincOps.ByteBltBlock, energy: CARDINAL] RETURNS [bytesTransferred: NAT];
Writes information from a given area into a stream. Not normally invoked from outside the package. Returns value smaller than block size, or zero, when pieces run out or if the current piece is for playback.
Check: PROC [handle: Handle] RETURNS[BOOLEAN];
If there are any errors pending for the given voice stream, they are signalled immediately. If there are no errors and there is data pending for input, then TRUE is returned. Otherwise, FALSE is returned. Not normally used outside this package.
StartServer: PROC [stream: IO.STREAM];
Called to initialize the voice stream server and let us know where to output diagnostic messages. Nothing will happen in voice streams until this routine gets called.
Handle: TYPE = REF VSRecord;
NotifyProc: TYPE = PROC [self: Handle, clientData: REF ANY];
Called when a voice stream runs out of pieces to play. The procedure is invoked with the stream locked!!
--------------------------------------------------------
Stuff below this point is only used by the implementation files and should never be used by clients.
--------------------------------------------------------
buffersPerStream: PRIVATE INTEGER = 3;
VSRecord:
PRIVATE
TYPE =
RECORD [
This record describes a voice transfer. There are three lists of buffers: the server buffers are those that are waiting to be processed by the server, the client buffers are those waiting to be processed by the client, and the idle buffers are those that aren't in use at all (usually because there are no voice pieces specified for the stream). All of the VSRecords are linked together so that the server process can find them. If errorRope isn't NIL, then an error has occurred for the voice stream, as described by errorRope and errorCode. Proc is a procedure to be invoked with clientData as parameter whenever the last piece for the steam has been eaten up. Socket identifies the remote socket that is destination/source for data transmission. Action is set by the get and put routines whenever data passes into or out of the stream, and it is cleared by the monitor routine.
jukebox: Jukebox.Handle,
firstServerBuffer: REF Buffer,
firstClientBuffer: REF Buffer,
firstIdleBuffer: REF Buffer,
piece: REF Piece,
next: REF VSRecord,
errorRope: Rope.ROPE,
errorCode: ErrorCode,
proc: NotifyProc,
notified: BOOL,
clientData: REF ANY,
connection: Connection,
newPiece: CONDITION,
action: BOOLEAN
];
Connection: TYPE = REF ConnectionObject;
ConnectionObject:
TYPE =
RECORD [
socket: PupDefs.PupSocket,
packetSize: NAT, -- Packet size when we're sending.
bufferSpace: NAT, -- Milliseconds of buffering in Lark.
probeLaunchTime: IntervalTimer.Pulses, -- time probe was transmitted
probeSequenceNumber: CARDINAL, -- last probe fingerprint
a packet transmitted at localEpoch would arrive at the Lark at larkEpoch
larkEpoch: CARDINAL, -- Lark clock (milliseconds)
localEpoch: IntervalTimer.Pulses,
roundTripMS: CARDINAL, -- milliseconds
initialProbeDone: CONDITION,
initialProbeFinished: BOOL,
drift: INT, -- accumulated clock error
sendNext: IntervalTimer.Pulses,
pollStreamNext: IntervalTimer.Pulses,
sequenceNumber: CARDINAL,
timeToPlay: CARDINAL, -- lark time to play next packet
packetMS: NAT -- Milliseconds in most recent packet
];
Buffer: PRIVATE TYPE = RECORD [
This record keeps track of one chirp and the space used to represent it. Block is only valid when the block is on the client list, and refers to the portion yet to be filled/emptied by the client. Valid and block are set by the server process before adding the buffer to the client list. A FALSE value for valid means that the server shouldn't write out the chirp. ToJukebox is TRUE if the buffer is to be filled with information for the jukebox, and FALSE if the buffer is already filled with information coming from the jukebox.
chirp: LONG INTEGER,
chirpSpace: VM.Interval,
window: Jukebox.WindowOrigin,
block: PrincOps.ByteBltBlock,
runData: RunData,
runIndex: Jukebox.RunArrayRange,
bytesAccountedFor: NAT,
next: REF Buffer,
toJukebox: BOOLEAN,
keyIndex: NAT,
valid: BOOLEAN,
playedBytes: CARDINAL
];
RunDataObject: TYPE = Jukebox.RunDataObject;
RunData:
TYPE = Jukebox.RunData;
Piece: PRIVATE TYPE = RECORD [
This record describes a continuous chunk of voice information within one tune for purposes of reading or writing via a VoiceStream. Piece information is interpreted entirely by the server process. The flush field means that the piece is supposed to be flushed, but hasn't been flushed yet.
tuneId: INT,
create: BOOLEAN,
firstByte: INT,
nBytes: INT,
tune: Jukebox.Tune,
playback: BOOLEAN,
keyIndex: NAT,
next: REF Piece,
flush: BOOLEAN,
ambientLevel: Jukebox.EnergyRange, -- for recording only, set by parameters to AddPiece
packetsSinceLastNonSilence: INT -- used in both recording and playback: indicates how long since last sound worth playing occured; preset to Jukebox.hangoverPackets by AddPiece
];
Monitor lock for all internal routines:
Lock: MONITORLOCK;
Condition variables used by clients and servers of voicestreams to wait for buffers to fill or empty:
serverCondition: CONDITION;
client: CONDITION;
Condition variable used by the WaitEmpty procedure:
waitCondition: CONDITION;
Condition variable used during Close to ensure that the server is finished touching the voice stream.
closeCondition: CONDITION;
IO stream used to ouput diagnostic messages:
ioStream: IO.STREAM;
Head of list of all open voicestreams:
VSList: Handle;
setError:
PRIVATE
PROC [handle: Handle, rope: Rope.
ROPE,
code: ErrorCode];
Records error information in a voice stream.
flushBuffer: PRIVATE PROC [handle: Handle];
Flushes leading client buffer to server.
END.
April 5, 1983 2:35 pm, Stewart, add RunData to Buffer record
June 4, 1983 5:39 pm, Stewart, add notified to VSRecord in order to notify only once
L. Stewart, June 20, 1983 4:52 pm, Try using Pulses instead of Microseconds
L. Stewart, December 26, 1983 7:41 pm, Cedar 5