<> <> <> <> <> <> <> <> 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