<> <> <> <> <> <> <> <> <> <> DIRECTORY FS USING [Read, Write], IO USING [int, PutFR, STREAM], Jukebox USING [bytesPerChirp, CloseTune, EOF, Error, FindChirp, MissingChirp, OpenTune, TuneSize, RunComponent, singlePktLength, RunElementType, RunArrayRange, Tune], Process USING [Detach, Priority, priorityNormal, SetPriority], Rope USING [ROPE], RuntimeError USING [ UNCAUGHT ], VM USING [AddressForPageNumber], VoiceStream; VoiceStreamServerImpl: MONITOR LOCKS VoiceStream.Lock IMPORTS FS, IO, Jukebox, Process, RuntimeError, VM, VoiceStream EXPORTS VoiceStream = BEGIN Foo: SIGNAL = CODE; debugRuns: BOOL _ FALSE; serverPriority: Process.Priority _ Process.priorityNormal; GetServerBuffer: ENTRY PROC RETURNS [handle: VoiceStream.Handle] = { <> ENABLE UNWIND => NULL; buffer: REF VoiceStream.Buffer; WHILE TRUE DO handle _ VoiceStream.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.intervalSpec.length > 0) AND (handle.firstIdleBuffer # NIL)) THEN RETURN [handle]; }; }; handle _ handle.next; ENDLOOP; <> <> BROADCAST VoiceStream.closeCondition; WAIT VoiceStream.serverCondition; ENDLOOP; }; GiveClientBuffer: ENTRY PROC [handle: VoiceStream.Handle] = { ENABLE UNWIND => NULL; buffer, b2: REF VoiceStream.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 VoiceStream.client; }; SetError: PUBLIC ENTRY PROC [handle: VoiceStream.Handle, rope: Rope.ROPE, code: VoiceStream.ErrorCode] = { <> <> <> <> ENABLE UNWIND => NULL; handle.errorRope _ rope; handle.errorCode _ code; BROADCAST VoiceStream.client; BROADCAST VoiceStream.waitCondition; }; GetPiece: ENTRY PROC [handle: VoiceStream.Handle] RETURNS [VoiceStream.Piece] = { <> <> <<>> <> ENABLE UNWIND => NULL; piece: VoiceStream.Piece; tune: Jukebox.Tune_NIL; WHILE TRUE DO piece _ handle.piece; IF piece = NIL THEN { handle.pieceInProgress _ FALSE; RETURN [NIL]; }; IF piece.flush THEN piece.intervalSpec.length _ 0; IF handle.pieceInProgress THEN { nxP: VoiceStream.Piece _ piece.next; IF piece.intervalSpec.length > 0 THEN RETURN [piece]; <> tune _ piece.tune; IF (handle.firstClientBuffer # NIL) OR (handle.firstServerBuffer # NIL) THEN RETURN [NIL]; -- No buffer-ahead IF handle.proc#NIL THEN -- Report completion of a piece handle.proc[handle, $finished, piece.clientData]; IF tune # NIL AND (nxP = NIL OR nxP.intervalSpec.tuneID # piece.intervalSpec.tuneID) THEN { Jukebox.CloseTune[handle.jukebox, tune]; tune _ NIL; }; handle.piece _ piece _ piece.next; }; handle.pieceInProgress _ TRUE; IF piece = NIL THEN { BROADCAST VoiceStream.client; LOOP; }; IF tune=NIL THEN tune _ Jukebox.OpenTune[handle.jukebox, piece.intervalSpec.tuneID, ~piece.playback]; piece.tune _ tune; IF handle.proc#NIL THEN -- Report start of next piece (see AddPiece for report of first) handle.proc[handle, $started, handle.piece.clientData]; ENDLOOP; RETURN [NIL]; }; Server: PROC [] = { <> handle: VoiceStream.Handle; buffer: REF VoiceStream.Buffer; ourSize: INT; skipLeading: INT; piece: VoiceStream.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.intervalSpec.tuneID], IO.int[piece.intervalSpec.start]]]; CONTINUE; }; <> Jukebox.EOF => { piece.intervalSpec.length _ 0; CONTINUE; }; RuntimeError.UNCAUGHT => { 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.intervalSpec.keyIndex; <> IF piece.tune = NIL OR NOT (piece.playback OR piece.tune.writable) THEN ERROR; <
> IF piece.intervalSpec.start < 0 THEN piece.intervalSpec.start _ Jukebox.TuneSize[piece.tune] * Jukebox.bytesPerChirp; buffer.chirp _ piece.intervalSpec.start / Jukebox.bytesPerChirp; <
> skipLeading _ piece.intervalSpec.start MOD Jukebox.bytesPerChirp; buffer.bytesAccountedFor _ skipLeading; ourSize _ Jukebox.bytesPerChirp - skipLeading; <<>> <> IF piece.intervalSpec.length < ourSize THEN { <> IF piece.playback THEN ourSize _ piece.intervalSpec.length ELSE piece.intervalSpec.length _ ourSize; IF piece.playback THEN buffer.bytesAccountedFor _ Jukebox.bytesPerChirp - ourSize; }; <> piece.intervalSpec.length _ piece.intervalSpec.length - ourSize; piece.intervalSpec.start _ piece.intervalSpec.start + 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.intervalSpec.ambientLevel; GiveClientBuffer[handle]; ENDLOOP; }; CheckRunData: PROC [buffer: REF VoiceStream.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] = { VoiceStream.ioStream _ stream; }; Init: PROC = { <> <> p: PROCESS; p _ FORK Server[]; Process.Detach[p]; }; Init[]; END. 1983, Stewart, run-encoded chirps May 10, 1986 4:14:09 pm PDT, Stewart, Cedar 5, December <> <> <> <> <> <<>> <<>>