<> <> <> <> <> <> <> <> <> DIRECTORY Basics USING [ BITSHIFT ], IO USING [noWhereStream, STREAM], Jukebox USING [bytesPerChirp, CloseTune, Handle, IntervalSpec, pagesPerChirp, RunComponent, singlePktLength, EnergyRange, hangoverPackets, VoiceDirection], PrincOps USING [ByteBltBlock], PrincOpsUtils USING [ByteBlt], Process USING [MsecToTicks, SetTimeout], Rope USING [ROPE], VM USING [AddressForPageNumber, Allocate, Free], VoiceStream; VoiceStreamBasicImpl: MONITOR LOCKS Lock IMPORTS Basics, Jukebox, IO, PrincOpsUtils, Process, VM EXPORTS VoiceStream = BEGIN <> Lock: PUBLIC MONITORLOCK; wholeTune: PUBLIC INT _ 100000000; serverCondition: PUBLIC CONDITION; client: PUBLIC CONDITION; waitCondition: PUBLIC CONDITION; closeCondition: PUBLIC CONDITION; ioStream: PUBLIC IO.STREAM _ IO.noWhereStream; vSList: PUBLIC VoiceStream.Handle _ NIL; Error: PUBLIC ERROR[reason: VoiceStream.ErrorCode, rope: Rope.ROPE, clientData: REF] = CODE; DemonError: PUBLIC ERROR = CODE; <> <> nOpens: INT _ 0; nWaits: INT _ 0; Open: PUBLIC PROC [jukebox: Jukebox.Handle, proc: VoiceStream.NotifyProc _ NIL] RETURNS [handle: VoiceStream.Handle] = { ENABLE UNWIND => NULL; buffer: REF VoiceStream.Buffer _ NIL; handle _ NEW[VoiceStream.VSRecord _ [jukebox: jukebox, errorCode: Bug, proc: proc]]; Process.SetTimeout[condition: @handle.newPiece, ticks: Process.MsecToTicks[400]]; FOR i:INTEGER IN [0..VoiceStream.buffersPerStream) DO buffer _ NEW[VoiceStream.Buffer]; buffer.valid _ FALSE; buffer.chirpSpace _ VM.Allocate[count: Jukebox.pagesPerChirp]; buffer.block.blockPointer _ LOOPHOLE[VM.AddressForPageNumber[buffer.chirpSpace.page]]; buffer.runData _ buffer.block.blockPointer + (Jukebox.bytesPerChirp/2); buffer.next _ handle.firstIdleBuffer; handle.firstIdleBuffer _ buffer; ENDLOOP; OpenLocked[handle]; RETURN [handle]; }; OpenLocked: ENTRY PROC [handle: VoiceStream.Handle] = { ENABLE UNWIND => NULL; handle.next _ vSList; vSList _ handle; nOpens _ nOpens + 1; <> NOTIFY serverCondition; }; Close: PUBLIC ENTRY PROC [handle: VoiceStream.Handle] = { <> <> ENABLE UNWIND => NULL; buffer: REF VoiceStream.Buffer; record: VoiceStream.Handle; <> <> IF (handle.errorRope # NIL) AND (handle.errorCode = StreamClosed) THEN ReportError[handle]; <> FlushBuffer[handle]; <> FlushProc[handle]; <> WHILE (handle.firstClientBuffer # NIL) DO handle.firstClientBuffer.valid _ FALSE; GiveServerBuffer[handle:handle]; ENDLOOP; <> <> <> WHILE (handle.firstServerBuffer # NIL) AND (handle.errorRope = NIL) DO NOTIFY serverCondition; WAIT closeCondition; ENDLOOP; <> IF handle.piece # NIL THEN { IF handle.piece.tune # NIL THEN Jukebox.CloseTune[handle.jukebox, handle.piece.tune]; }; <> IF handle.connection # NIL THEN handle.connection.socket _ NIL; <> <> <> IF vSList = handle THEN vSList _ handle.next ELSE { record _ vSList; WHILE record.next # handle DO IF record.next = NIL THEN { handle.errorCode _ Bug; handle.errorRope _ "Unexpected vSList end."; ReportError[handle]; }; record _ record.next; ENDLOOP; record.next _ handle.next; }; WHILE handle.firstClientBuffer # NIL DO buffer _ handle.firstClientBuffer; handle.firstClientBuffer _ buffer.next; VM.Free[buffer.chirpSpace]; ENDLOOP; WHILE handle.firstServerBuffer # NIL DO buffer _ handle.firstServerBuffer; handle.firstServerBuffer _ buffer.next; VM.Free[buffer.chirpSpace]; ENDLOOP; WHILE handle.firstIdleBuffer # NIL DO buffer _ handle.firstIdleBuffer; handle.firstIdleBuffer _ buffer.next; VM.Free[buffer.chirpSpace]; ENDLOOP; <> handle.errorCode _ StreamClosed; handle.errorRope _ "Can't use voice stream after it's closed."; handle.jukebox _ NIL; BROADCAST client; BROADCAST waitCondition; }; Get: PUBLIC ENTRY PROC [handle: VoiceStream.Handle, maxSilentBytes: NAT, block: PrincOps.ByteBltBlock, wait: BOOL _ FALSE] RETURNS [silence: NAT _ 0, bytesTransferred: NAT _ 0, keyIndex: NAT _ 0] = { ENABLE UNWIND => NULL; buffer: REF VoiceStream.Buffer; runSize: NAT; DO <<1) Aquire a non-empty chirp>> <<>> <> <<>> WHILE (handle.firstClientBuffer = NIL) DO IF handle.errorRope # NIL THEN ReportError[handle]; IF handle.piece = NIL THEN { BROADCAST waitCondition; RETURN; }; nWaits _ nWaits + 1; IF wait THEN WAIT client ELSE RETURN; ENDLOOP; buffer _ handle.firstClientBuffer; keyIndex _ buffer.keyIndex; <> <> IF buffer.toJukebox THEN RETURN; <> <> IF handle.piece.flush THEN { handle.firstClientBuffer.valid _ FALSE; GiveServerBuffer[handle: handle]; LOOP; }; <<>> <> IF buffer.toJukebox THEN RETURN; <> IF buffer.bytesAccountedFor >= Jukebox.bytesPerChirp THEN { <> GiveServerBuffer[handle: handle]; LOOP; }; WITH curr: buffer.runData.runArray[buffer.runIndex] SELECT FROM silence => { silenceAmount: CARDINAL; IF maxSilentBytes=0 THEN RETURN; runSize _ curr.length; silenceAmount _ MIN[runSize, maxSilentBytes, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; silence _ silence + silenceAmount; curr.length _ runSize - silenceAmount; IF curr.length=0 THEN { buffer.runIndex_buffer.runIndex+1; buffer.playedBytes_0 }; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + silenceAmount; handle.piece.packetsSinceLastNonSilence _ handle.piece.packetsSinceLastNonSilence + (silenceAmount+Jukebox.singlePktLength/2)/Jukebox.singlePktLength; maxSilentBytes _ maxSilentBytes - silenceAmount; }; soundEnergy => { runSize _ NARROW[buffer.runData.runArray[buffer.runIndex+1], Jukebox.RunComponent[soundLength]].length; handle.piece.packetsSinceLastNonSilence _ IF curr.energy <= buffer.runData.ambientLevel THEN handle.piece.packetsSinceLastNonSilence + (runSize+Jukebox.singlePktLength/2)/Jukebox.singlePktLength ELSE 0; IF handle.piece.packetsSinceLastNonSilence > Jukebox.hangoverPackets <> THEN { silenceAmount: CARDINAL; IF maxSilentBytes=0 THEN RETURN; silenceAmount _ MIN[runSize, maxSilentBytes, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; buffer.block.startIndex _ buffer.block.startIndex + silenceAmount; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + silenceAmount; silence _ silence + silenceAmount; buffer.runData.runArray[buffer.runIndex+1] _ [soundLength[runSize - silenceAmount]]; IF NARROW[buffer.runData.runArray[buffer.runIndex+1], Jukebox.RunComponent[soundLength]].length=0 THEN { buffer.runIndex_buffer.runIndex+2; buffer.playedBytes_0 }; maxSilentBytes _ maxSilentBytes - silenceAmount; } ELSE -- really want to play this back { voiceAmount: CARDINAL; origStopIndex: CARDINAL; count: CARDINAL; maxSilentBytes _ 0; <> voiceAmount _ MIN[runSize, block.stopIndexPlusOne - block.startIndex, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; IF runSize # voiceAmount AND curr.energy <= buffer.runData.ambientLevel THEN <> handle.piece.packetsSinceLastNonSilence _ handle.piece.packetsSinceLastNonSilence - ((runSize - voiceAmount)+Jukebox.singlePktLength/2)/Jukebox.singlePktLength; origStopIndex _ block.stopIndexPlusOne; block.stopIndexPlusOne _ block.startIndex + voiceAmount; count _ PrincOpsUtils.ByteBlt[from: buffer.block, to: block]; IF count # voiceAmount THEN ERROR; block.stopIndexPlusOne _ origStopIndex; block.startIndex _ block.startIndex + voiceAmount; buffer.block.startIndex _ buffer.block.startIndex + voiceAmount; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + voiceAmount; buffer.runData.runArray[buffer.runIndex+1] _ [soundLength[runSize - voiceAmount]]; IF NARROW[buffer.runData.runArray[buffer.runIndex+1], Jukebox.RunComponent[soundLength]].length=0 THEN { buffer.runIndex_buffer.runIndex+2; buffer.playedBytes_0 }; bytesTransferred _ bytesTransferred + voiceAmount; IF block.startIndex >= block.stopIndexPlusOne THEN { IF buffer.bytesAccountedFor >= Jukebox.bytesPerChirp THEN { <> GiveServerBuffer[handle: handle]; }; handle.action _ TRUE; RETURN; } } }; singlePkt => { handle.piece.packetsSinceLastNonSilence _ IF curr.energy <= buffer.runData.ambientLevel THEN handle.piece.packetsSinceLastNonSilence + 1 ELSE 0; IF handle.piece.packetsSinceLastNonSilence > Jukebox.hangoverPackets <> THEN { silenceAmount: CARDINAL; IF maxSilentBytes=0 THEN RETURN; runSize _ Jukebox.singlePktLength - buffer.playedBytes; silenceAmount _ MIN[runSize, maxSilentBytes, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; buffer.block.startIndex _ buffer.block.startIndex + silenceAmount; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + silenceAmount; buffer.playedBytes _ buffer.playedBytes + silenceAmount; silence _ silence + silenceAmount; IF buffer.playedBytes = Jukebox.singlePktLength THEN { buffer.runIndex_buffer.runIndex+1; buffer.playedBytes_0 }; maxSilentBytes _ maxSilentBytes - silenceAmount; } ELSE -- really want to play this back { voiceAmount: CARDINAL; origStopIndex: CARDINAL; count: CARDINAL; maxSilentBytes _ 0; runSize _ Jukebox.singlePktLength - buffer.playedBytes; voiceAmount _ MIN[runSize, block.stopIndexPlusOne - block.startIndex, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; IF voiceAmount < Jukebox.singlePktLength/2 AND curr.energy <= buffer.runData.ambientLevel THEN <> handle.piece.packetsSinceLastNonSilence _ handle.piece.packetsSinceLastNonSilence - 1; origStopIndex _ block.stopIndexPlusOne; block.stopIndexPlusOne _ block.startIndex + voiceAmount; count _ PrincOpsUtils.ByteBlt[from: buffer.block, to: block]; IF count # voiceAmount THEN ERROR; block.stopIndexPlusOne _ origStopIndex; block.startIndex _ block.startIndex + voiceAmount; buffer.block.startIndex _ buffer.block.startIndex + voiceAmount; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + voiceAmount; buffer.playedBytes _ buffer.playedBytes + voiceAmount; IF buffer.playedBytes = Jukebox.singlePktLength THEN { buffer.runIndex_buffer.runIndex+1; buffer.playedBytes_0 }; bytesTransferred _ bytesTransferred + voiceAmount; IF block.startIndex >= block.stopIndexPlusOne THEN { IF buffer.bytesAccountedFor >= Jukebox.bytesPerChirp THEN { <> GiveServerBuffer[handle: handle]; }; handle.action _ TRUE; RETURN; } } }; ENDCASE => ERROR; handle.action _ TRUE; ENDLOOP; }; Put: PUBLIC ENTRY PROC [handle: VoiceStream.Handle, silentBytes: NAT, block: PrincOps.ByteBltBlock, energy: CARDINAL] RETURNS [bytesTransferred: NAT _ 0] = { <> <> ENABLE UNWIND => NULL; buffer: REF VoiceStream.Buffer; energy _ Basics.BITSHIFT[energy, -2]; <> <<>> WHILE silentBytes > 0 OR block.startIndex < block.stopIndexPlusOne DO WHILE handle.firstClientBuffer = NIL DO IF handle.errorRope # NIL THEN ReportError[handle]; IF handle.piece = NIL THEN { BROADCAST waitCondition; RETURN; }; WAIT client; ENDLOOP; <> <> buffer _ handle.firstClientBuffer; IF NOT buffer.toJukebox THEN RETURN; <> <<(throw it away if it's invalid, otherwise fill it with zeroes).>> IF handle.piece.flush THEN { IF handle.firstClientBuffer.valid THEN FlushBuffer[handle] ELSE GiveServerBuffer[handle: handle]; LOOP; }; <> IF NOT buffer.toJukebox THEN RETURN; <<>> <> IF buffer.bytesAccountedFor >= Jukebox.bytesPerChirp THEN { <> GiveServerBuffer[handle: handle]; LOOP; }; <<>> SELECT TRUE FROM silentBytes>0 => { silenceAmount: CARDINAL _ MIN[silentBytes, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; handle.piece.packetsSinceLastNonSilence _ handle.piece.packetsSinceLastNonSilence + (silenceAmount+(Jukebox.singlePktLength/2))/Jukebox.singlePktLength; 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]] }; silentBytes _ silentBytes - silenceAmount; buffer.runData.runArray[buffer.runIndex]_ [silence[NARROW[buffer.runData.runArray[buffer.runIndex], Jukebox.RunComponent[silence]].length+silenceAmount]]; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + silenceAmount; }; block.startIndex < block.stopIndexPlusOne => { IF energy <= buffer.runData.ambientLevel THEN { transferAmount: CARDINAL _ MIN[block.stopIndexPlusOne - block.startIndex, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; handle.piece.packetsSinceLastNonSilence _ handle.piece.packetsSinceLastNonSilence + (transferAmount+(Jukebox.singlePktLength/2))/Jukebox.singlePktLength } ELSE handle.piece.packetsSinceLastNonSilence _ 0; <> IF handle.piece.packetsSinceLastNonSilence > Jukebox.hangoverPackets THEN { <> silenceAmount: CARDINAL _ MIN[block.stopIndexPlusOne - block.startIndex, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; 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+silenceAmount]]; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + silenceAmount; bytesTransferred _ bytesTransferred + silenceAmount; block.startIndex _ block.startIndex + silenceAmount; <> IF block.startIndex >= block.stopIndexPlusOne THEN { IF buffer.bytesAccountedFor >= Jukebox.bytesPerChirp THEN { -- IO.PutF[ioStream, "Put chirp %d.\n", IO.int[buffer.chirp]]; GiveServerBuffer[handle: handle]; }; handle.action _ TRUE; RETURN } } ELSE { voiceAmount: CARDINAL _ MIN[block.stopIndexPlusOne - block.startIndex, Jukebox.bytesPerChirp - buffer.bytesAccountedFor]; origStopIndex: CARDINAL _ block.stopIndexPlusOne; count: CARDINAL; block.stopIndexPlusOne _ block.startIndex + voiceAmount; count _ PrincOpsUtils.ByteBlt[from: block, to: buffer.block]; IF count # voiceAmount THEN ERROR; block.stopIndexPlusOne _ origStopIndex; IF voiceAmount > 0 THEN buffer.valid _ TRUE; block.startIndex _ block.startIndex + voiceAmount; buffer.block.startIndex _ buffer.block.startIndex + voiceAmount; buffer.bytesAccountedFor _ buffer.bytesAccountedFor + voiceAmount; bytesTransferred _ bytesTransferred + voiceAmount; IF ~(buffer.runData.runArray[buffer.runIndex].elementType = silence AND NARROW[buffer.runData.runArray[buffer.runIndex], Jukebox.RunComponent[silence]].length=0) THEN buffer.runIndex _ buffer.runIndex +(IF buffer.runData.runArray[buffer.runIndex].elementType = soundEnergy THEN 2 ELSE 1); <> IF voiceAmount = Jukebox.singlePktLength THEN buffer.runData.runArray[buffer.runIndex] _ [singlePkt[energy]] ELSE { buffer.runData.runArray[buffer.runIndex] _ [soundEnergy[energy]]; buffer.runData.runArray[buffer.runIndex+1] _ [soundLength[voiceAmount]] }; IF block.startIndex >= block.stopIndexPlusOne THEN { IF buffer.bytesAccountedFor >= Jukebox.bytesPerChirp THEN { <> GiveServerBuffer[handle: handle]; }; handle.action _ TRUE; RETURN; }; } }; ENDCASE => ERROR; handle.action _ TRUE; ENDLOOP; }; AddPiece: PUBLIC ENTRY PROC [ handle: VoiceStream.Handle, intervalSpec: Jukebox.IntervalSpec, direction: Jukebox.VoiceDirection, clientData: REF ] = { <> <> ENABLE UNWIND => NULL; piece, p2: VoiceStream.Piece; IF handle.errorRope # NIL THEN ReportError[handle]; piece _ NEW[VoiceStream.PieceObject _ [ intervalSpec: intervalSpec, clientData: clientData, playback: direction = play, packetsSinceLastNonSilence: Jukebox.hangoverPackets ]]; IF handle.piece = NIL THEN { handle.pieceInProgress _ FALSE; handle.piece _ piece; } ELSE { FOR p2 _ handle.piece, p2.next WHILE p2.next#NIL DO ENDLOOP; p2.next _ piece; }; <> <> NOTIFY serverCondition; }; FlushPieces: PUBLIC ENTRY PROC [handle: VoiceStream.Handle] = { ENABLE UNWIND => NULL; FlushProc[handle]; }; FlushProc: INTERNAL PROC [handle: VoiceStream.Handle] = { <> <> piece: VoiceStream.Piece _ handle.piece; FOR p: VoiceStream.Piece _ handle.piece, p.next WHILE p # NIL DO p.flush _ TRUE; piece _ p; ENDLOOP; BROADCAST client; BROADCAST waitCondition; IF handle.proc#NIL THEN handle.proc[handle, $flushed, IF piece#NIL THEN piece.clientData ELSE NIL] }; IsEmpty: PUBLIC PROC [handle: VoiceStream.Handle] RETURNS [BOOLEAN] = { <
> <> IF handle.firstClientBuffer # NIL THEN RETURN[FALSE]; IF handle.piece # NIL THEN RETURN[FALSE]; RETURN[TRUE]; }; WaitEmpty: PUBLIC ENTRY PROC [handle: VoiceStream.Handle] = { <> ENABLE UNWIND => NULL; WHILE TRUE DO IF handle.errorRope # NIL THEN RETURN; IF (handle.firstClientBuffer = NIL) AND (handle.piece = NIL) THEN RETURN; WAIT waitCondition; ENDLOOP; }; Check: PUBLIC ENTRY PROC [handle: VoiceStream.Handle] RETURNS[BOOLEAN] = { <> <> <> <> ENABLE UNWIND => NULL; IF handle.errorRope # NIL THEN ReportError[handle]; IF handle.firstClientBuffer = NIL THEN RETURN[FALSE]; IF handle.firstClientBuffer.toJukebox THEN RETURN[FALSE]; RETURN[TRUE]; }; GiveServerBuffer: INTERNAL PROC [handle: VoiceStream.Handle] = { <> <> buffer: REF VoiceStream.Buffer _ handle.firstClientBuffer; handle.firstClientBuffer _ buffer.next; buffer.next _ NIL; IF handle.firstServerBuffer = NIL THEN handle.firstServerBuffer _ buffer ELSE { b2: REF VoiceStream.Buffer; FOR b2 _ handle.firstServerBuffer, b2.next WHILE b2.next#NIL DO ENDLOOP; b2.next _ buffer; }; NOTIFY serverCondition; }; FlushBuffer: PUBLIC INTERNAL PROC [handle: VoiceStream.Handle] = { <> <> buffer: REF VoiceStream.Buffer _ handle.firstClientBuffer; IF buffer = NIL OR ~buffer.toJukebox THEN RETURN; IF buffer.bytesAccountedFor IN (0..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 GiveServerBuffer[handle: handle]; }; }; ReportError: PUBLIC PROC[handle: VoiceStream.Handle] = { piece: VoiceStream.Piece; FOR p: VoiceStream.Piece_handle.piece, piece.next WHILE piece#NIL DO piece_p; ENDLOOP; ERROR Error[handle.errorCode, handle.errorRope, IF piece#NIL THEN piece.clientData ELSE NIL]; }; END. Last Edited by: L. Stewart, March 25, 1983 3:54 pm, VoiceStream change L. Stewart, April 5, 1983 2:38 pm, Tioga formatting, rundata L. Stewart, April 11, 1983 1:06 pm, bug fixing in rundata Last Edited by: L. Stewart, April 19, 1983 3:17 pm, Silence encoding L. Stewart, June 4, 1983 5:42 pm, call notifyProc only once L. Stewart, May 10, 1986 4:06:25 pm PDT, Cedar 5, VoiceStreamBasicImpl, AddPiece, FlushPieces, FlushProc, IsEmpty, WaitEmpty, Check, L, EXPORTS <> <> <> <> <> <> <<>> <<>> <<>>