VoiceRopeImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Doug Terry, October 14, 1986 3:59:55 pm PDT
Swinehart, July 24, 1987 4:14:18 pm PDT
Polle Zellweger (PTZ) July 27, 1987 2:44:12 pm PDT
Client code for interacting with the Voice Rope Service.
DIRECTORY
FinchSmarts USING [ConvDesc, ObtainServiceInterface, ReportSystemStateProc, ReportConversationStateProc, ReportRequestStateProc, RegisterForReports, ServiceConnect],
Atom USING [GetPName, GetPropFromList, PutPropOnList],
Process USING [SecondsToTicks, SetTimeout, Ticks],
PupSocket USING [GetUniqueID],
RefID USING [ID, nullID],
Rope USING [ROPE, Concat],
RPC USING [CallFailed, ImportFailed],
Thrush USING [ActionType, ConversationID, InterfaceSpec, none, nullConvID, SHHH],
VoiceRopeServer,
VoiceRopeServerRpcControl USING [ImportInterface],
VoiceUtils USING [Problem],
VoiceRope;
VoiceRopeImpl: CEDAR MONITOR
IMPORTS Atom, FinchSmarts, Process, PupSocket, Rope, RPC, VoiceRopeServer, VoiceRopeServerRpcControl, VoiceUtils
EXPORTS VoiceRope
~
BEGIN
ROPE: TYPE ~ Rope.ROPE;
recordingService: ROPE = "recording";
recordingServiceInterface: ROPE = "VoiceRopeServer";
RequestID: TYPE = VoiceRope.RequestID;
PendingRequest: TYPE = REF PendingRequestBody;
PendingRequestBody:
TYPE =
RECORD [
id: RequestID,
state: Thrush.ActionType ← $unknown
];
PendingRequests: TYPE = LIST OF PendingRequest;
VoiceRopeInfo: TYPE = REF VoiceRopeInfoBody;
VoiceRopeInfoBody:
TYPE =
RECORD [
imported: BOOLEAN ← FALSE,
serviceID: RefID.ID ← RefID.nullID,
shhh: Thrush.SHHH ← Thrush.none
];
VoiceRopeDesc: TYPE = REF VoiceRopeDescBody;
VoiceRopeDescBody:
TYPE =
RECORD [
cDesc: FinchSmarts.ConvDesc ← NIL, -- only valid during operations below.
serviceID: RefID.ID ← RefID.nullID,
shhh: Thrush.SHHH ← Thrush.none,
pendingRequests: PendingRequests ← NIL,
reportArrived: CONDITION,
clientData: REF←NIL
];
info: VoiceRopeInfo ← NEW[VoiceRopeInfoBody];
Probably needs more reporting when RPC calls fail and reconnects fail, too.
VoiceRope client interface
Handle: TYPE = VoiceRope.Handle; -- handles are no longer used for anything
Open:
PUBLIC
PROC [voiceRopeDBName:
ROPE ←
NIL, localName:
ROPE ←
NIL,
Complain:
PROC[complaint:
ROPE] ←
NIL]
RETURNS [handle: Handle] ~ {
If voiceRopeDBName or localName is omitted, a default based on the user profile choice of Thrush Server is invented. If Complain is omitted, Log.Problem[...$Finch] is used.
RETURN[NIL]; -- this routine is no longer needed!
};
Record:
PUBLIC
PROC[handle: Handle←
NIL]
RETURNS [voiceRope: VoiceRope.VoiceRope←NIL] ~ { [voiceRope: voiceRope] ← RecordNB[handle: handle]; };
Play:
PUBLIC PROC[handle: Handle←
NIL, voiceRope: VoiceRope.VoiceRope,
queueIt:
BOOL←
TRUE, wait:
BOOL←
FALSE] ~ {
[] ← PlayNB[handle: handle, voiceRope: voiceRope, queueIt: queueIt, wait: wait]; };
Stop:
PUBLIC PROC[handle: Handle←
NIL] ~ { [] ← StopNB[handle: handle]; };
RecordNB:
PUBLIC
PROC [handle: Handle←
NIL, requestID: RequestID←VoiceRope.nullRequestID, convID: Thrush.ConversationID← Thrush.nullConvID, clientData:
REF←
NIL]
RETURNS [nb: VoiceRope.
NB, voiceRope: VoiceRope.VoiceRope←
NIL, newConvID: Thrush.ConversationID← Thrush.nullConvID] ~ {
Records a voice rope, registers it , and returns its ID. A NIL return value indicates that something went wrong.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
vrDesc: VoiceRopeDesc ← GetConversation[convID, clientData];
Create or find conversation, import service interface, record client data if provided
IF vrDesc = NIL THEN RETURN [$noConv, NIL];
newConvID ← vrDesc.cDesc.situation.self.convID;
requestID ← AddRequest[vrDesc, requestID];
[nb
, voiceRope] ←
VoiceRopeServer
.Record[vrDesc.shhh,
vrDesc.cDesc.situation.self, vrDesc.serviceID, requestID, TRUE];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to record failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
wait for report that says it finished (or was abandoned)
WaitForRequestState[vrDesc: vrDesc, state: $finished, id: requestID];
[nb, voiceRope.length] ← VoiceRopeServer.Length[vrDesc
.shhh, voiceRope
! RPC.CallFailed => {nb ← $callFailed; CONTINUE}];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to get voicerope length failed - ",Atom.GetPName[nb]], $VoiceRope];
nb ← $noLength;
RETURN;
};
vrDesc.cDesc ← NIL; -- Break cycles when conversation goes away
};
PlayNB:
PUBLIC
PROC [handle: Handle←
NIL, voiceRope: VoiceRope.VoiceRope, queueIt:
BOOL←
TRUE, wait:
BOOL←
FALSE, requestID: RequestID←VoiceRope.nullRequestID, convID: Thrush.ConversationID←Thrush.nullConvID, clientData:
REF←
NIL]
RETURNS [nb: VoiceRope.
NB, newConvID: Thrush.ConversationID ← Thrush.nullConvID] ~ {
Play a specified voice rope. The boolean arguments are interpreted as follows:
queueIt => play after all other record/playback requests are satisfied.
wait => wait until things appear to be started properly, or have failed.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
vrDesc: VoiceRopeDesc ← GetConversation[convID, clientData];
IF vrDesc = NIL THEN RETURN [$noConv];
newConvID ← vrDesc.cDesc.situation.self.convID;
requestID ← AddRequest[vrDesc, requestID];
nb ← VoiceRopeServer
.Play[
vrDesc.shhh, voiceRope, vrDesc.cDesc.situation.self, vrDesc.serviceID, requestID, queueIt];
IF nb#$success
THEN {
VoiceUtils.Problem[Rope.Concat["Attempt to play voicerope failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
IF wait THEN WaitForRequestState[vrDesc: vrDesc, state: $started, id: requestID];
vrDesc.cDesc ← NIL;
};
StopNB:
PUBLIC
PROC [handle: Handle ←
NIL, convID: Thrush.ConversationID← Thrush.nullConvID]
RETURNS [nb: VoiceRope.
NB] ~ {
Stops any recording or playback in progress.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
vrDesc: VoiceRopeDesc ← GetConversation[convID, NIL, FALSE];
IF vrDesc=NIL THEN RETURN [$noConv];
nb ← VoiceRopeServer.Stop[vrDesc.shhh, vrDesc.cDesc.situation.self, vrDesc.serviceID];
IF nb#$success
THEN {
VoiceUtils.Problem[Rope.Concat["Attempt to stop failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
vrDesc.cDesc ← NIL;
Retain:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope.VoiceRope, class: VoiceRope.InterestClass, refID:
ROPE, other:
ROPE ←
NIL] ~ {
Registers a new interest in the voice rope. The voice rope will be retained until either a corresponding Forget is done or the class' garbage collection process determines that the voice rope is no longer referenced, e.g. refID no longer exists. Taken together, the vr, class, and refID must be unique. Repeated calls of Retain with the same parameters (ignoring other) will only register a single interest.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
nb ← VoiceRopeServer.Retain[info.shhh, vr, class, refID, other];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to retain voicerope failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
Forget:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope.VoiceRope, class: VoiceRope.InterestClass, refID:
ROPE] ~ {
The specified refID of the specified class drops its interest in the voice rope. The voice rope is not necessarily deleted, however, since other interests in the same voice rope may exist.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
nb ← VoiceRopeServer.Forget[info.shhh, vr, class, refID];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to forget voicerope failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
GetByInterest:
PUBLIC
PROC [handle: Handle ←
NIL, class: VoiceRope.InterestClass, refID:
ROPE]
RETURNS [voiceRope: VoiceRope.VoiceRope] ~ {
Returns any voice rope that is of interest to the given class and refID; returns NIL if no such voice rope exists.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
[nb, voiceRope] ← VoiceRopeServer.GetByInterest[info.shhh, class, refID];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to lookup voicerope by interest failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
Cat:
PUBLIC
PROC [handle: Handle ←
NIL, vr1, vr2, vr3, vr4, vr5: VoiceRope.VoiceRope ←
NIL]
RETURNS [new: VoiceRope.VoiceRope←
NIL] ~ {
Concatenates together the non-NIL voice ropes to produce a new voice rope.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
[nb, new] ← VoiceRopeServer.Cat[info.shhh, vr1, vr2, vr3, vr4, vr5];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to cat voiceropes failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
Substr:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope.VoiceRope, start:
INT ← 0, len:
INT ←
LAST[
INT]]
RETURNS [new: VoiceRope.VoiceRope←
NIL] ~ {
Creates a new voice rope that is a substring of an existing voice rope.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
[nb, new] ← VoiceRopeServer.Substr[info.shhh, vr, start, len];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to substring voicerope failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
Replace:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope.VoiceRope, start:
INT ← 0, len:
INT ←
LAST[
INT], with: VoiceRope.VoiceRope ←
NIL]
RETURNS [new: VoiceRope.VoiceRope←
NIL] ~ {
Creates a new voice rope in which the given interval of the voice rope "vr" is replaced by the voice rope "with".
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
[nb, new] ← VoiceRopeServer.Replace[info.shhh, vr, start, len, with];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to replace part of a voicerope failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
Length:
PUBLIC
PROC [handle: Handle ←
NIL, vr: VoiceRope.VoiceRope]
RETURNS [len:
INT𡤁] ~ {
Returns the actual length of the voice rope. This operation ignores the start and length values specified in the voice rope. Thus, vr.start ← 0; vr.length ← Length[handle, vr] will restore a voice rope to its full contents.
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
IF NOT RecordingServiceInterface[] THEN RETURN;
[nb, len] ← VoiceRopeServer.Length[info.shhh, vr ! RPC.CallFailed => {nb ← $callFailed; CONTINUE}];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to get voicerope length failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
DescribeRope:
PUBLIC
PROC [
handle: Handle ← NIL, vr: VoiceRope.VoiceRope, minSilence: INT ← -1]
RETURNS [noise: VoiceRope.IntervalSpecs←NIL] ~ {
ENABLE RPC.CallFailed => { IF ~info.imported THEN CONTINUE; info.imported←FALSE; RETRY; };
nb: VoiceRope.NB;
len: INT;
IF NOT RecordingServiceInterface[] THEN RETURN;
[nb, len, noise] ← VoiceRopeServer.DescribeRope[info.shhh, vr, minSilence];
IF nb#$success
THEN
{
VoiceUtils.Problem[Rope.Concat["Attempt to describe voicerope failed - ",Atom.GetPName[nb]], $VoiceRope];
RETURN;
};
};
Report handling
Action reports are generated by the voice rope server when a request for $recording/$playback is $scheduled, $started, $finished, or $flushed. Reports are also available from FinchSmarts concerning the state of a conversation or Finch in general.
A list of pending requests is maintained as part of VoiceRopeInfo. This list is sorted so that the most recent request is at the head of the list. Upon receipt of a $finished or $flushed report, the associated request (and all previous requests) are removed from the pending request queue. All pending requests are flushed if the established conversation goes idle (i.e. the user hangs up). The condition, reportArrived, is raised whenever a new report is received. Procedures that wish to wait for a given action report may wait on this condition; they should then check the state of the pending request queue and take appropriate action (such as waiting again).
SystemReport: FinchSmarts.ReportSystemStateProc ~ {
Don't care about changes in system state at the moment.
NULL;
};
ConversationReport: FinchSmarts.ReportConversationStateProc ~ {
[ nb: NB, cDesc: ConvDesc, remark: ROPE←NIL ]
vrDesc: VoiceRopeDesc ← GetVRDesc[cDesc];
IF vrDesc = NIL THEN RETURN;
SELECT cDesc.situation.self.state
FROM
$active => NULL;
$idle, $neverWas, $failed => {
-- throw away pending requests
RemoveRequests[vrDesc, NIL];
NotifyWaiters[vrDesc];
};
ENDCASE;
};
RequestReport: FinchSmarts.ReportRequestStateProc ~ {
[ cDesc: ConvDesc, actionReport: Thrush.ActionReport, actionRequest: REF ] RETURNS [betterActionRequest: REF]
request: PendingRequest;
vrDesc: VoiceRopeDesc ← GetVRDesc[cDesc];
IF vrDesc = NIL THEN RETURN;
SELECT actionReport.actionClass
FROM
$recording, $playback => {
request ← GetRequestByID[vrDesc, actionReport.actionID];
IF request = NIL THEN RETURN[NIL];
request.state ← actionReport.actionType;
SELECT request.state
FROM
$scheduled, $started => NULL;
Throw away pending requests that are complete. This includes requests preceding the one being reported on.
$finished, $flushed =>
RemoveRequests[vrDesc, request];
ENDCASE;
NotifyWaiters[vrDesc];
};
ENDCASE;
RETURN[NIL]; -- leave actionRequest alone
};
The following routines manage the queue of pending requests. (They are the only monitor entry procedures in this module.)
GetRequestByID:
ENTRY PROC [vrDesc: VoiceRopeDesc, id: RequestID]
RETURNS [request: PendingRequest ←
NIL] ~ {
IF vrDesc=NIL THEN RETURN;
FOR q: PendingRequests ← vrDesc.pendingRequests, q.rest
WHILE q #
NIL
DO
IF q.first.id = id THEN RETURN[q.first];
ENDLOOP;
};
AddRequest:
ENTRY
PROC [vrDesc: VoiceRopeDesc, id: RequestID]
RETURNS [requestID: RequestID] ~ {
request: PendingRequest ←
NEW[PendingRequestBody ←
[requestID ← IF id=VoiceRope.nullRequestID THEN NewRequestID[] ELSE id]];
vrDesc.pendingRequests ← CONS[request, vrDesc.pendingRequests];
};
RemoveRequests:
ENTRY PROC [vrDesc: VoiceRopeDesc, request: PendingRequest]
RETURNS [] ~ {
IF vrDesc=NIL THEN RETURN;
IF request =
NIL
OR vrDesc.pendingRequests =
NIL
OR vrDesc.pendingRequests.first = request
THEN
vrDesc.pendingRequests ← NIL
ELSE
FOR q: PendingRequests ← vrDesc.pendingRequests, q.rest
WHILE q.rest #
NIL
DO
IF q.rest.first = request
THEN {
q.rest ← NIL; -- truncate queue to remove this and all previous requests
EXIT;
};
ENDLOOP;
};
NotifyWaiters:
ENTRY PROC [vrDesc: VoiceRopeDesc]
RETURNS [] ~ {
ENABLE UNWIND => NULL;
IF vrDesc=NIL THEN RETURN;
BROADCAST vrDesc.reportArrived;
};
Wait:
ENTRY PROC [vrDesc: VoiceRopeDesc]
RETURNS [] ~ {
ENABLE UNWIND => NULL;
IF vrDesc=NIL THEN RETURN;
WAIT vrDesc.reportArrived;
};
WaitForRequestState:
PROC [vrDesc: VoiceRopeDesc, state: Thrush.ActionType, id: RequestID]
RETURNS [] ~ {
request: PendingRequest;
timeout: Process.Ticks ← IF state = $started THEN Process.SecondsToTicks[2] ELSE Process.SecondsToTicks[10];
IF vrDesc=NIL THEN RETURN;
TRUSTED { Process.SetTimeout[@vrDesc.reportArrived, timeout]; };
DO
request ← GetRequestByID[vrDesc, id];
IF request = NIL THEN EXIT; -- completed or conv went idle
SELECT state
FROM
$scheduled =>
SELECT request.state
FROM
$scheduled, $started, $finished, $flushed => EXIT;
ENDCASE => NULL; -- unknown?
$started =>
SELECT request.state
FROM
$started, $finished, $flushed => EXIT;
ENDCASE => NULL; -- probably $scheduled
$finished =>
SELECT request.state
FROM
$finished, $flushed => EXIT;
ENDCASE => NULL; -- probably $scheduled or $started
ENDCASE => EXIT;
Wait[vrDesc];
ENDLOOP;
};
GetClientData:
PUBLIC
PROC[handle: Handle←
NIL, cDesc: FinchSmarts.ConvDesc]
RETURNS[clientData: REF←NIL] = {
vrDesc: VoiceRopeDesc ← GetVRDesc[cDesc];
IF vrDesc#NIL THEN { -- vrDesc.cDesc←NIL; -- RETURN[vrDesc.clientData]; };
};
Establishing conversations and RPC connections
GetVRDesc:
PROC[cDesc: FinchSmarts.ConvDesc]
RETURNS[vrDesc: VoiceRopeDesc←
NIL] = {
IF cDesc#
NIL
THEN
vrDesc ← NARROW[Atom.GetPropFromList[cDesc.props, $VoiceRopeDesc]];
IF vrDesc#NIL THEN vrDesc.cDesc ← cDesc;
};
GetConversation:
PROC [
convID: Thrush.ConversationID, clientData: REF←NIL, createOK: BOOL←TRUE]
RETURNS [vrDesc: VoiceRopeDesc ← NIL] ~ {
nb: VoiceRope.NB;
cDesc: FinchSmarts.ConvDesc;
[nb, cDesc] ← FinchSmarts.ServiceConnect[recordingService, convID, createOK];
IF nb#$success
THEN {
VoiceUtils.Problem[Rope.Concat["Can't establish conversation - ",Atom.GetPName[nb]], $VoiceRope];
RETURN[NIL];
};
vrDesc ← GetVRDesc[cDesc];
IF vrDesc=
NIL
THEN {
vrDesc ← NEW[VoiceRopeDescBody ← []];
cDesc.props ← Atom.PutPropOnList[cDesc.props, $VoiceRopeDesc, vrDesc];
vrDesc.cDesc ← cDesc;
};
IF clientData#NIL THEN vrDesc.clientData ← clientData;
IF ~RecordingServiceInterface[vrDesc] THEN cDesc ← NIL;
};
RecordingServiceInterface:
PROC[vrDesc: VoiceRopeDesc ←
NIL]
RETURNS [imported:
BOOLEAN ←
TRUE] = {
nb: VoiceRope.NB;
interfaceSpec: Thrush.InterfaceSpec;
cDesc: FinchSmarts.ConvDesc ← IF vrDesc=NIL THEN NIL ELSE vrDesc.cDesc;
IF info.imported AND vrDesc#NIL AND vrDesc.serviceID#RefID.nullID THEN RETURN[TRUE];
[nb, info.shhh, interfaceSpec] ←
FinchSmarts.ObtainServiceInterface[recordingService, recordingServiceInterface, cDesc];
IF nb#$success THEN RETURN[FALSE];
info.serviceID ← interfaceSpec.serviceID;
IF vrDesc#NIL THEN { vrDesc.serviceID ← info.serviceID; vrDesc.shhh ← info.shhh; };
IF info.imported THEN RETURN[TRUE];
info.imported ← TRUE;
VoiceRopeServerRpcControl.ImportInterface[interfaceName: interfaceSpec.interfaceName, hostHint: interfaceSpec.hostHint
!
RPC.ImportFailed => {
info.imported ← FALSE;
VoiceUtils.Problem["Can't import recording service.", $VoiceRope];
CONTINUE}];
RETURN[info.imported];
};
NewRequestID:
PUBLIC
PROC
RETURNS [VoiceRope.RequestID] = {
RETURN[LOOPHOLE[PupSocket.GetUniqueID[]]]};
Initializations
FinchSmarts.RegisterForReports[c: ConversationReport, r: RequestReport];
END.
Doug Terry, August 14, 1986 9:40:42 am PDT
changes to: DIRECTORY, VoiceRopeClientImpl, EXPORTS, ~, Open, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, Proc, Proc
Doug Terry, August 14, 1986 9:58:55 am PDT
changes to: DIRECTORY, VoiceRopeClientImpl, EXPORTS, ~, Open, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, VoiceRopeInterface, Proc, Proc, END
Doug Terry, August 27, 1986 11:43:29 am PDT
changes to: DIRECTORY, VoiceRopeImpl, ~, Open, Play, Stop, VoiceRopeInterface, DescribeRope
Doug Terry, August 29, 1986 4:13:15 pm PDT
changes to: ~, Play, VoiceRopeImpl, Record, Stop, NewID, Proc, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface
Doug Terry, September 5, 1986 4:28:37 pm PDT
changes to: DIRECTORY, IMPORTS, EXPORTS, ~, Handle, Open, Proc
Doug Terry, September 25, 1986 2:59:31 pm PDT
changes to: DIRECTORY, SystemReport, ConversationReport, RequestReport, FinchSmarts, Record
Doug Terry, September 29, 1986 5:56:59 pm PDT
changes to: SystemReport, Record, ConversationReport, RequestReport, ~
Doug Terry, October 3, 1986 3:51:47 pm PDT
changes to: ~, GetRequestByID, GetRequestByRef, NewRequest, RemoveRequests, NewID, Record, Play, ConversationReport, RequestReport, WaitForRequestState, DIRECTORY, IMPORTS, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface, Stop, WaitForRequestState, Handle, Open
Doug Terry, October 3, 1986 4:45:36 pm PDT
changes to: RequestReport
Doug Terry, October 6, 1986 1:44:44 pm PDT
changes to: DIRECTORY, ~, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface, IMPORTS
Doug Terry, October 9, 1986 8:00:18 pm PDT
changes to: Record, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope
Doug Terry, October 9, 1986 8:45:25 pm PDT
changes to: Wait, WaitForRequestState
Doug Terry, October 14, 1986 3:59:55 pm PDT
changes to: ConversationReport
Polle Zellweger (PTZ) July 20, 1987 5:57:28 pm PDT
Changes to allow TiogaVoice to monitor conversation state/reports about voicerope operations.
changes to: Record, Play, Stop, NewID=>NewRequestID
Polle Zellweger (PTZ) July 20, 1987 6:08:42 pm PDT
changes to: DIRECTORY, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, GetConversation, RecordingServiceInterface, NewRequestID
Polle Zellweger (PTZ) July 21, 1987 12:04:32 pm PDT
changes to: DIRECTORY, Record, Play, Record, Play, Stop, StopNB
Swinehart, July 24, 1987 12:12:38 pm PDT
Merge disparate changes to VoiceRope. Improve error and connection recovery; adapt to interface changes in FinchSmarts.
changes to: DIRECTORY, ~, Open, Record, Play, queueIt, Stop, RecordNB, PlayNB, StopNB, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, GetConversation, RecordingServiceInterface, NewRequestID
Polle Zellweger (PTZ) July 27, 1987 12:20:16 pm PDT
changes to: PlayNB, RecordNB, StopNB, GetClientData, GetConversation