<> <> <> <> <> <> <> DIRECTORY FS USING [Read, Write], IO USING [int, PutFR, STREAM], Jukebox USING [bytesPerChirp, CloseTune, CreateTune, EOF, Error, FindChirp, MissingChirp, OpenTune, TuneSize], 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.runIndex MOD 2) # 0 THEN { IF buffer.runData.runArray[buffer.runIndex] = 0 THEN buffer.runIndex _ buffer.runIndex - 1 ELSE { buffer.runIndex _ buffer.runIndex + 1; buffer.runData.runArray[buffer.runIndex] _ 0; }; }; buffer.runData.runArray[buffer.runIndex] _ buffer.runData.runArray[buffer.runIndex] + (Jukebox.bytesPerChirp - buffer.bytesAccountedFor); }; 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]; }; <
> 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; 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] _ ourSize; buffer.runData.packetSize _ 0; } ELSE { -- build a fake chirp with the right structure buffer.block.startIndex _ 0; buffer.block.stopIndexPlusOne _ ourSize; buffer.runData.runArray[0] _ skipLeading; buffer.runData.packetSize _ 0; }; } 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; silenceRunType: BOOL; <> buffer.block.startIndex _ 0; DO runSize _ buffer.runData.runArray[buffer.runIndex]; silenceRunType _ (buffer.runIndex MOD 2) = 0; IF skipLeading >= runSize THEN { <> skipLeading _ skipLeading - runSize; IF NOT silenceRunType THEN buffer.block.startIndex _ buffer.block.startIndex + runSize; buffer.runIndex _ buffer.runIndex + 1; } ELSE { -- last run IF NOT silenceRunType THEN buffer.block.startIndex _ buffer.block.startIndex + skipLeading; buffer.runData.runArray[buffer.runIndex] _ runSize - skipLeading; 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] _ 0; buffer.runData.packetSize _ 0; }; buffer.toJukebox _ NOT piece.playback; giveClientBuffer[handle]; ENDLOOP; }; PrivRunDataObject: PRIVATE TYPE = RECORD [ <> runArray: ARRAY [0..95) OF CARDINAL, packetSize: CARDINAL ]; CheckRunData: PROC [buffer: REF VoiceStream.Buffer] = { mrd: LONG POINTER TO PrivRunDataObject _ LOOPHOLE[buffer.runData]; total: CARDINAL _ 0; run: CARDINAL; FOR ri: NAT IN [0..95) DO run _ mrd.runArray[ri]; IF run > Jukebox.bytesPerChirp THEN { mrd.runArray[ri] _ Jukebox.bytesPerChirp - total; IF debugRuns THEN SIGNAL Foo; EXIT; }; total _ total + run; IF total > Jukebox.bytesPerChirp THEN { mrd.runArray[ri] _ run - (total - Jukebox.bytesPerChirp); 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