DIRECTORY IO USING [noWhereStream, STREAM], Jukebox USING [bytesPerChirp, CloseTune, Handle, pagesPerChirp, RunComponent, singlePktLength, EnergyRange, hangoverPackets], PrincOps USING [ByteBltBlock], PrincOpsUtils USING [ByteBlt], Process USING [MsecToTicks, SetTimeout], Rope USING [ROPE], VM USING [AddressForPageNumber, Allocate, Free], VoiceStream; VoiceStreamBasicImpl: MONITOR LOCKS Lock IMPORTS 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] = CODE; DemonError: PUBLIC ERROR = CODE; nOpens: INT _ 0; nWaits: INT _ 0; Open: PUBLIC PROC [jukebox: Jukebox.Handle, proc: VoiceStream.NotifyProc _ NIL, clientData: REF ANY _ NIL] RETURNS [handle: VoiceStream.Handle] = { ENABLE UNWIND => NULL; buffer: REF VoiceStream.Buffer _ NIL; handle _ NEW[VoiceStream.VSRecord _ [jukebox, NIL, NIL, NIL, NIL, NIL, NIL, Bug, proc, FALSE, clientData, NIL, , FALSE]]; 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 ERROR Error[StreamClosed, handle.errorRope]; 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 ERROR Error[Bug, "Unexpected vSList end."]; 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 WHILE (handle.firstClientBuffer = NIL) DO IF handle.errorRope # NIL THEN ERROR Error[handle.errorCode, handle.errorRope]; IF handle.piece = NIL THEN { IF handle.proc # NIL AND NOT handle.notified THEN { handle.notified _ TRUE; handle.proc[handle, handle.clientData]; }; BROADCAST waitCondition; RETURN; }; nWaits _ nWaits + 1; IF wait THEN WAIT client ELSE RETURN; ENDLOOP; buffer _ handle.firstClientBuffer; handle.notified _ FALSE; 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 _ energy/4; WHILE silentBytes > 0 OR block.startIndex < block.stopIndexPlusOne DO WHILE handle.firstClientBuffer = NIL DO IF handle.errorRope # NIL THEN ERROR Error[handle.errorCode, handle.errorRope]; IF handle.piece = NIL THEN { IF handle.proc # NIL AND NOT handle.notified THEN { handle.notified _ TRUE; handle.proc[handle, handle.clientData]; }; BROADCAST waitCondition; RETURN; }; WAIT client; ENDLOOP; buffer _ handle.firstClientBuffer; handle.notified _ FALSE; IF NOT buffer.toJukebox THEN RETURN; 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, tuneId: INT, firstByte: INT, nBytes: INT, create: BOOLEAN, playback: BOOLEAN _ TRUE, keyIndex: NAT _ 0, flush: BOOLEAN _ FALSE, ambientLevel: Jukebox.EnergyRange _ 0] = { ENABLE UNWIND => NULL; piece, p2: REF VoiceStream.Piece; IF handle.errorRope # NIL THEN ERROR Error[handle.errorCode, handle.errorRope]; IF flush THEN FlushProc[handle]; piece _ NEW[VoiceStream.Piece _ [tuneId, create, firstByte, nBytes, NIL, playback, keyIndex, NIL, FALSE, ambientLevel, Jukebox.hangoverPackets]]; IF handle.piece = NIL THEN handle.piece _ piece ELSE { p2 _ handle.piece; WHILE p2.next # NIL DO p2 _ p2.next ENDLOOP; p2.next _ piece; }; NOTIFY serverCondition; }; FlushPieces: PUBLIC ENTRY PROC [handle: VoiceStream.Handle] = { ENABLE UNWIND => NULL; FlushProc[handle]; }; FlushProc: INTERNAL PROC [handle: VoiceStream.Handle] = { p: REF VoiceStream.Piece _ handle.piece; WHILE p # NIL DO p.flush _ TRUE; p _ p.next; ENDLOOP; BROADCAST client; BROADCAST waitCondition; }; 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 ERROR Error[handle.errorCode, handle.errorRope]; IF handle.firstClientBuffer = NIL THEN RETURN[FALSE]; IF handle.firstClientBuffer.toJukebox THEN RETURN[FALSE]; RETURN[TRUE]; }; GiveServerBuffer: INTERNAL PROC [handle: VoiceStream.Handle] = { buffer, b2: REF VoiceStream.Buffer; buffer _ handle.firstClientBuffer; handle.firstClientBuffer _ buffer.next; buffer.next _ NIL; IF handle.firstServerBuffer = NIL THEN handle.firstServerBuffer _ buffer ELSE { b2 _ handle.firstServerBuffer; WHILE b2.next # NIL DO b2 _ b2.next ENDLOOP; b2.next _ buffer; }; NOTIFY serverCondition; }; FlushBuffer: PUBLIC INTERNAL PROC [handle: VoiceStream.Handle] = { buffer: REF VoiceStream.Buffer; buffer _ handle.firstClientBuffer; IF buffer = NIL THEN RETURN; IF ~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]; }; }; 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 ΌFile: VoiceStreamBasicImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. This file contains part of the VoiceStream implementation. Routines in this file provide basic client facilities such as opening closing, reading, and writing voice streams. Ades, April 25, 1986 3:21:45 pm PST Swinehart, May 10, 1986 5:38:29 pm PDT Last Edited by: Swinehart, April 14, 1983 10:48 am Last Edited by: Ousterhout, March 8, 1983 11:37 amcompile Last Edited by: L. Stewart, December 30, 1983 11:19 am See VoiceStream.mesa for documentation on the following things. The following variables keep track of how many streams have been used and how often waiting occurred. Invoke the server to prepare the buffers for actual use. This procedure closes out a voice stream. It waits for pending I/O to complete, then deallocates the stream. IO.PutF[ioStream, "Closing tune.\n"]; Make sure that the stream isn't already closed. Output any partially full buffers to the jukebox. Flush any pending pieces. Flush any pending client buffers. Wait for the server to get completely caught up. This code also synchronizes with the server so we're sure the server isn't touching the voice stream info anymore. Close any tune that might be open. If there is a socket process, then signal it to die. Remove the VSRecord from our list, then de-allocate the spaces for the buffers (everything else is taken care of by the garbage collector. Mark the VSRecord invalid. 1) Aquire a non-empty chirp If the server isn't doing its job, then we may have to wait here to get a chirp. This shouldn't happen and should probably be reported. Rather than wait, we should play silence and try again later in order to keep the protocol inviolate. If the stream is currently going the wrong way, then just return immediately whith whatever has been accumulated. Typically this is a RETURN[0, 0]. If the current piece is being flushed, then just give the buffers back to the server immediately (ignore the data). If presently recording, then quietly return. Typically this is a RETURN[0, 0]. If we have used up all of this chirp, then give it back and get the next chirp. This should be done at the end of the code, not the beginning. IO.PutF[ioStream, "Got chirp %d.\n", IO.int[buffer.chirp]]; means packet should be treated as silence that should not be transmitted once sound found, stop on further occurences of silence we have added too much to packetsSinceLastNonSilence: this is never exact in the face of variable length soundLengths and partial usage of singlePkts, but we'd like to keep it near as possible IO.PutF[ioStream, "Got chirp %d.\n", IO.int[buffer.chirp]]; means packet should be treated as silence that should not be transmitted we incremented packetsSinceLastNonSilence incorrectly: if we take things out of the chirp exclusively in less than Jukebox.singlePktLength/2 lengths we'll get our counting wrong, but we'll at least try to be roughly right most of the time IO.PutF[ioStream, "Got chirp %d.\n", IO.int[buffer.chirp]]; This procedure adds the bytes from block to the end of the voice stream. since we store energies as 14 bit unsigned numbers, but they come in as cardinals If the stream is currently going the wrong way, then just return immediately. Typically this is a RETURN[0]. If the current piece is being flushed, then get rid of the current buffer (throw it away if it's invalid, otherwise fill it with zeroes). If not presently recording, then quietly return. Typically this is a RETURN[0]. If we have used up all of this chirp, then give it back and get the next chirp. This should be done at the end of the code, not the beginning. IO.PutF[ioStream, "Got chirp %d.\n", IO.int[buffer.chirp]]; last thing placed in chirp was non-silent beware: this bit of the code assumes blocks of sound passed to Put will not exceed 30ms in length. If they do it can happen that blocks will be discarded because of low energy sooner than hangoverPackets from when the last over-threshold sound occured. Such a radical change is unlikely to occur to VoiceStreams and handling it here would make the code even more obscure. we want to throw away this sound, because it is now more than the hangover time since a packet with sufficient energy occured last thing placed in chirp was non-silent alter only the source count, to look as though we have taken the sound only false if this is first item to be placed in this chirp IO.PutF[ioStream, "Put chirp %d.\n", IO.int[buffer.chirp]]; This routine allocates another piece descriptor and adds it to the list for the voice stream. ioStream.PutF["%s: size %d\n", IO.rope[IF playback THEN "Playback" ELSE "Record"], IO.int[nBytes]]; This routine flushes any unused voice info in a stream. This is done by marking all the pieces as "flushed". Then, various routines see the flush flag and ignore information. Buffers currently waiting to be written to disk will not be flushed, but everything else will be. It isn't safe synchronization-wise for us to just remove all the buffers and pieces, hence the use of the flag. Figure out whether the socket process has used up all the available pieces. Just wait until the stream empties completely. If there are any errors pending for the given voice stream, they are signalled immediately. If there are no errors and there is data ready for playback, then TRUE is returned. Otherwise, FALSE is returned. This routine passes the leading buffer to the server, and activates the server. If there is a partially-filled leading client buffer, this routine pads it with zeros and sends it to the server. last thing placed in chirp was non-silent Swinehart, May 10, 1986 4:03:07 pm PDT Convert to new communication package changes to: BEGIN, vSList, Open, OpenLocked, Close, Get, Put, GiveServerBuffer, FlushBuffer Κί˜šœ™Icodešœ Οmœ1™<—Jšœ:™:Jšœ=™=šœ4™4K™#K™&—J˜Jšœ2™2Jšœ9™9Jšœ6™6J˜šΟk ˜ Jšžœžœžœ˜!Jšœžœp˜}Jšœ žœ˜Jšœžœ ˜Jšœžœ˜(Jšœžœžœ˜Jšžœžœ(˜0J˜ J˜—Jšœžœžœ˜(Jšžœ žœž˜/šžœ˜J˜—Jšž˜J˜Jšœ?™?J˜Jšœžœž œ˜Jšœ žœžœ ˜"Jšœžœž œ˜"Jšœžœž œ˜Jšœžœž œ˜ Jšœžœž œ˜!Jš œ žœžœžœžœ˜.Jšœžœžœ˜(Jš œžœžœ+žœžœ˜KJšœ žœžœžœ˜ J˜Jšœ@™@Jšœ$™$J˜Jšœžœ˜Jšœžœ˜J˜šΟnœžœžœ;žœ˜PJšœ žœžœžœ˜Jšžœ!˜(J˜Jšžœžœžœ˜J˜Jšœžœžœ˜%J˜šœ žœ"žœžœžœžœžœžœ˜KJšœ žœžœžœ˜-—JšœQ˜Qšžœžœžœ#ž˜5Jšœ žœ˜!Jšœžœ˜Jšœžœ(˜>Jšœžœžœ/˜VJšœG˜GJ˜%J˜ Jšžœ˜—šœ˜J˜—Jšžœ ˜Jšœ˜—J˜šŸ œžœžœ!˜7Jšžœžœžœ˜J˜Jšœ˜Jšœ˜J˜J˜Jšœ8™8J˜Jšžœ˜Jšœ˜—J˜JšŸœžœžœžœ!˜9˜Jšœ7™7Jšœ5™5J˜Jšžœžœžœ˜J˜Jšœžœ˜Jšœ˜J˜Jšœ%™%J˜Jšœ/™/J˜šžœžœžœ"˜AJšžœžœ(˜2J˜—Jšœ1™1J˜J˜J˜Jšœ™J˜J˜J˜Jšœ!™!J˜šžœžœž˜)Jšœ!žœ˜'J˜ Jšžœ˜J˜—Jšœ;™;Jšœ:™:Jšœ-™-J˜š žœžœžœžœž˜FJšžœ˜Jšžœ˜Jšžœ˜J˜—Jšœ"™"J˜šžœžœžœ˜Jšžœžœžœ6˜U˜J˜——Jšœ4™4J˜Jšžœžœžœžœ˜?J˜Jšœ>™>Jšœ@™@Jšœ ™ J˜Jšžœžœ˜,šžœ˜J˜šžœž˜Jšžœžœžœžœ&˜EJ˜—Jšžœ˜J˜Jšœ˜J˜—šžœžœž˜'J˜"J˜'Jšžœ˜Jšžœ˜J˜—šžœžœž˜'J˜"J˜'Jšžœ˜Jšžœ˜J˜—šžœžœž˜%J˜ J˜%Jšžœ˜Jšžœ˜J˜Jšœ™J˜—J˜ J˜?Jšœžœ˜Jšž œ˜Jšž œ˜Jšœ˜J˜—šŸœžœžœžœ.žœ&žœžœ˜zJšžœ žœžœžœ ˜LJšžœžœžœ˜J˜Jšœžœ˜Jšœ žœ˜ J˜šž˜J™J™J™οJ™šžœžœž˜)Jšžœžœžœžœ+˜OJ˜šžœžœžœ˜š žœžœžœžœžœ˜3Jšœžœ˜Jšœ'˜'J˜—Jšž œ˜Jšžœ˜Jšœ˜—J˜J˜Jšžœžœžœ˜Jšžœžœ˜ Jšžœ˜—J˜J˜"Jšœžœ˜J˜J˜Jšœ9™9JšœZ™ZJ˜Jšžœžœžœ˜ J˜JšœA™AJšœ1™1J˜šžœžœ˜Jšœ!žœ˜'J˜!Jšžœ˜Jšœ˜—J™JšœO™OJšžœžœžœ˜ J˜J™Žšžœ3žœ˜;Jšœ<™˜zJšœD˜DJšœ–˜–Jšœ0˜0J˜——Jšœ˜šœ žœ,˜>Jšœ,˜,Jšœ*žœ,žœgžœ˜Κ—˜JšžœC˜EJ™HJšž˜šœžœ˜Jšžœžœžœ˜ JšœžœL˜_JšœB˜BJšœD˜DJšœ"˜"JšœT˜TJšžœžœYž˜fšœ$˜$Jšœ˜—Jšœ˜Jšœ0˜0—J˜J˜JšžœΟc ˜%šœžœ˜Jšœžœ˜Jšœžœ˜Jšœ˜Jš 7™7Jšœžœg˜xšžœžœ,ž˜LJšœΐ™ΐJšœ ˜ —Jšœ'˜'Jšœ8˜8Jšœ=˜=Jšžœžœžœ˜"Jšœ'˜'Jšœ2˜2Jšœ@˜@JšœB˜BJšœR˜RJšžœžœYž˜fšœ$˜$Jšœ˜—Jšœ˜Jšœ2˜2šžœ,žœ˜4šžœ3žœ˜;Jšœ<™˜rJšœ0˜0—J˜J˜Jšžœ  ˜%šœžœ˜Jšœžœ˜Jšœžœ˜Jšœ˜Jšœ7˜7Jšœžœg˜xšžœ)žœ,ž˜^Jšœξ™ξJšœV˜V—Jšœ'˜'Jšœ8˜8Jšœ=˜=Jšžœžœžœ˜"Jšœ'˜'Jšœ2˜2Jšœ@˜@JšœB˜BJšœ6˜6Jšžœ.ž˜4šœ$˜$Jšœ˜—Jšœ˜—Jšœ2˜2šžœ,žœ˜4šžœ3žœ˜;Jšœ<™˜AJ˜!—Jšœ˜Jšœžœ˜Jšž˜—J˜J˜Jšž˜Jšœ˜Jšœ žœžœ^˜yJšœžœ˜1Jšœžœ˜Jšœ9˜9Jšœ=˜=Jšžœžœžœ˜"Jšœ'˜'Jšžœžœžœ˜,Jšœ2˜2Jšœ@˜@JšœB˜BJšœ2˜2JšœžœBžœžœTžœ%žœDžœžœ˜‘Jšœ;™;Jšžœ'žœ@žœE˜Άš œPžœ,žœžœ3žœ˜Αšœ;™;J˜!Jšœ˜—Jšœžœ˜Jšžœ˜J˜—J˜J˜—Jšžœžœ˜—J˜Jšœžœ˜Jšžœ˜—Jšœ˜J˜—š Ÿœžœžœžœ&žœ˜EJšœ žœ žœ žœ˜-Jš œ žœžœ žœ žœžœ,˜oJ˜JšœB™BJšœ™J˜Jšžœžœžœ˜J˜Jšœ žœ˜!J˜šžœž˜Jšžœžœ+˜5J˜—Jšžœžœ˜ J˜šœžœ8˜CJšžœžœžœ*˜M—Jšžœžœžœ˜/šžœ˜J˜Jšžœ žœžœžœ˜,J˜Jšœ˜—JšœR™RJšœ™Jšžœ˜Jšœ˜J˜—šŸ œžœžœžœ!˜?Jšžœžœžœ˜J˜Jšœ˜J˜—Jšœ žœžœ!˜9˜Jšœ7™7Jšœ;™;Jšœ;™;Jšœ8™8Jšœ7™7Jšœ:™:Jšœ(™(J˜J˜Jšœžœ"˜(šžœžœž˜Jšœ žœ˜J˜ Jšžœ˜J˜—Jšž œ˜Jšž œ˜Jšœ˜J˜—Jš Ÿœžœžœžœžœ˜G˜Jšœ9™9Jšœ™J˜Jš žœžœžœžœžœ˜5Jš žœžœžœžœžœ˜)Jšžœžœ˜ Jšœ˜J˜—JšŸ œžœžœžœ!˜=˜Jšœ.™.J˜Jšžœžœžœ˜šžœžœž˜ Jšžœžœžœžœ˜&Jš žœžœžœžœžœžœ˜IJšžœ˜Jšžœ˜—Jšœ˜J˜—šŸœžœžœžœ˜5Jšžœžœ˜J˜Jšœ;™;Jšœ;™;Jšœ8™8Jšœ™J˜Jšžœžœžœ˜šžœžœž˜Jšžœ+˜0—Jš žœžœžœžœžœ˜5Jšžœ$žœžœžœ˜9Jšžœžœ˜ Jšœ˜J˜—Jšœžœžœ!˜@˜Jšœ9™9Jšœ™J˜Jšœ žœ˜#J˜J˜"J˜'Jšœžœ˜Jšžœžœžœ"˜Hšžœ˜J˜Jšžœ žœžœžœ˜,J˜Jšœ˜—Jšžœ˜Jšœ˜J˜—Jšœ žœžœžœ!˜B˜Jšœ:™:Jšœ6™6J˜Jšœžœ˜J˜J˜"Jšžœ žœžœžœ˜Jšžœžœžœ˜!šžœžœžœ˜@JšžœAž˜GJ™)Jš œ'žœDžœžœužœ†˜ωJšœ2 ˜DJšœ!˜!J˜—šœ˜J˜——Jšžœ˜JšœF˜FJšœ<˜