FinchSynthesizerImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Swinehart, July 19, 1987 9:29:57 pm PDT
DIRECTORY
Atom USING [ GetPropFromList, PutPropOnList ],
FinchSynthesizer,
FinchSmarts USING [ ConvDesc, ObtainServiceInterface, RegisterForReports, ReportConversationStateProc, ReportRequestStateProc, ReportSystemStateProc, ServiceConnect ],
PupSocket USING [ GetUniqueID ],
RefID USING [ ID ],
RPC USING [ CallFailed, ImportFailed ],
Thrush USING [ ConversationID, InterfaceSpec, NB, nullConvID, nullID, SHHH ],
Synthesizer USING [ ActionID, nullActionID, SynthSpec, SynthSpecBody, SynthSpecs ],
SynthesizerServerRpcControl USING [
ImportNewInterface, InterfaceRecord, GetSynthesizerSpecifications, StopSpeech, TextToSpeech ]
;
FinchSynthesizerImpl: CEDAR MONITOR
Confusion about locks here and in FinchSmartsImpl; careful!
IMPORTS SynthesizerServerRpcControl, Atom, FinchSmarts, PupSocket, RPC
EXPORTS FinchSynthesizer = {
Declarations
ConvDesc: TYPE = FinchSmarts.ConvDesc;
ConversationID: TYPE = Thrush.ConversationID;
NB: TYPE = Thrush.NB;
nullID: RefID.ID = Thrush.nullID;
SHHH: TYPE = Thrush.SHHH;
SynthSpecs: TYPE = Synthesizer.SynthSpecs; -- LIST OF Synthesizer.SynthSpec
NconcSpecs: PROC[l1, l2: SynthSpecs] RETURNS [SynthSpecs] = INLINE {
IF l1=NIL THEN RETURN[l2];
FOR specs: SynthSpecs ← l1, specs.rest WHILE specs#NIL DO
IF specs.rest=NIL THEN { specs.rest←l2; RETURN[l1]; };
ENDLOOP;
ERROR;
};
synthesizerServer: SynthesizerServerRpcControl.InterfaceRecord ← NIL;
defaultTranslateProc: FinchSynthesizer.SynthesizerTranslateProc ← NIL;
shhh: SHHH;
Supervision
SysStateReport: ENTRY FinchSmarts.ReportSystemStateProc = {
At present, all that's necessary is to be sure no SCDescs remain in next iteration. Any actions will have been abandoned. We count on FinchSmarts to do that for us.
};
ConvStateReport: ENTRY FinchSmarts.ReportConversationStateProc = {
[nb: NB, cDesc: ConvDesc, remark: ROPE]
ENABLE UNWIND => NULL;
sCDesc: FinchSynthesizer.SConvDesc = GetSCDesc[cDesc];
IF sCDesc=NIL THEN RETURN; -- No biggee
SELECT cDesc.situation.self.state FROM
$failed, $idle => -- If pending specs still exist, report aborted upstream (somehow)???
sCDesc.pendingSpecs ← NIL; -- For now, just zap them; problem is monitor locks.
ENDCASE;
BROADCAST sCDesc.stateChange; -- This will be enough to alert interested parties
};
ReportReport: ENTRY FinchSmarts.ReportRequestStateProc = {
[cDesc: ConvDesc, actionReport: Thrush.ActionReport, actionRequest: REF]
RETURNS[betterActionRequest: REF]
This one should get the reports first. Then the client that requested action reports can get them too.
ENABLE UNWIND => NULL;
SELECT actionReport.actionClass FROM
$synthesizer => {
sCDesc: FinchSynthesizer.SConvDesc = GetSCDesc[cDesc];
IF sCDesc=NIL THEN RETURN; -- presumably nothing was pending!
BROADCAST sCDesc.stateChange;
FOR rqs: Synthesizer.SynthSpecs ← sCDesc.pendingSpecs, rqs.rest WHILE rqs#NIL DO
IF actionReport.actionID#rqs.first.actionID THEN LOOP;
betterActionRequest ← rqs.first;
SELECT actionReport.actionType FROM
Throw away pending intervals that are complete. This includes intervals preceding the one being reported on.
$finished, $flushed => sCDesc.pendingSpecs ← rqs.rest; ENDCASE;
EXIT;
ENDLOOP;
IF betterActionRequest=NIL THEN ERROR; -- Complain?["missing reports"]
};
ENDCASE=> cDesc ← cDesc; -- a place to stand during debugging
Endcase events are now expected, since there is no event-filtering in FinchSmarts; these will be events for other applications.
};
Client Functions
TextToSpeech: PUBLIC PROC
-- TAKES -- [
convID: Thrush.ConversationID←Thrush.nullConvID,
synthSpec: Synthesizer.SynthSpec,
queueIt: BOOLTRUE,
synthesizerTranslateProc: FinchSynthesizer.SynthesizerTranslateProc←NIL,
synthesizerModel: ATOMNIL, -- Not presently interpreted
synthesizerVersion: ATOMNIL
]
RETURNS [
nb: Thrush.NB←$success,
newConvID: Thrush.ConversationID,
newActionID: Synthesizer.ActionID
] = {
cDesc: ConvDesc;
sCDesc: FinchSynthesizer.SConvDesc;
ScheduleSpeech: ENTRY PROC RETURNS[nb: NB←$success] = {
ENABLE {
UNWIND=>NULL;
RPC.CallFailed => {
IF synthesizerServer=NIL THEN { nb ← $serverDown; CONTINUE; };
synthesizerServer ← NIL;
RETRY; -- Will attempt reconnection via main Finch connection—once
};
};
nb ← synthesizerServer.TextToSpeech[
shhh, cDesc.situation.self, sCDesc.synthesizerServiceID, synthSpec.textToSpeak, synthSpec.actionID, TRUE, queueIt];
IF nb=$success THEN
sCDesc.pendingSpecs ← NconcSpecs[sCDesc.pendingSpecs, LIST[synthSpec]];
};
[nb,cDesc,sCDesc]←SynthesizerConnect[convID];
IF nb=$success THEN {
newConvID ← cDesc.situation.self.convID;
synthSpec ← NEW[Synthesizer.SynthSpecBody ← synthSpec^];
newActionID ←
IF synthSpec.actionID=Synthesizer.nullActionID THEN (synthSpec.actionID ← NewID[])
ELSE synthSpec.actionID;
IF synthesizerTranslateProc#NIL THEN
synthSpec.textToSpeak ← synthesizerTranslateProc[synthSpec.textToSpeak]
ELSE IF defaultTranslateProc#NIL THEN
synthSpec.textToSpeak ← synthesizerTranslateProc[synthSpec.textToSpeak];
nb ← ScheduleSpeech[];
};
};
StopSpeech: PUBLIC PROC[convID: Thrush.ConversationID] RETURNS [nb: Thrush.NB] ={
RETURN[DoStopSpeech[convID: convID, reset: FALSE]];
};
ResetSynthesizer: PUBLIC PROC[convID: Thrush.ConversationID] RETURNS [nb: Thrush.NB] ={
RETURN[DoStopSpeech[convID: convID, reset: TRUE]];
};
DoStopSpeech: PROC[
convID: Thrush.ConversationID, reset: BOOL] RETURNS [nb: Thrush.NB] ={
ENABLE RPC.CallFailed => {
IF synthesizerServer=NIL THEN { nb ← $serverDown; CONTINUE; };
synthesizerServer ← NIL;
RETRY; -- Will attempt reconnection via main Finch connection—once
};
cDesc: ConvDesc;
sCDesc: FinchSynthesizer.SConvDesc;
IF ([nb,cDesc, sCDesc]←SynthesizerConnect[convID]).nb#$success OR cDesc.situation.self.state#$active THEN RETURN;
nb ← synthesizerServer.StopSpeech[
shhh, cDesc.situation.self, sCDesc.synthesizerServiceID, reset];
};
GetSynthesizerSpecifications: PUBLIC PROC[conversationID: Thrush.ConversationID]
RETURNS [nb: Thrush.NB←$success, synthesizerModel: ATOM, synthesizerVersion: ATOM] = {
Describe the assigned synthesizer.
ENABLE RPC.CallFailed => {
IF synthesizerServer=NIL THEN { nb ← $serverDown; CONTINUE; };
synthesizerServer ← NIL;
RETRY; -- Will attempt reconnection via main Finch connection—once
};
cDesc: ConvDesc;
sCDesc: FinchSynthesizer.SConvDesc;
IF ([nb,cDesc, sCDesc]←SynthesizerConnect[conversationID]).nb#$success OR cDesc.situation.self.state#$active THEN RETURN;
[nb, synthesizerModel, synthesizerVersion] ←
synthesizerServer.GetSynthesizerSpecifications[shhh, sCDesc.synthesizerServiceID];
};
RegisterTranslateProc: PUBLIC PROC [
conversationID: Thrush.ConversationID, translate: FinchSynthesizer.SynthesizerTranslateProc←NIL] = {
defaultTranslateProc ← translate;
};
Utilities
SynthesizerConnect: PROC[convID: Thrush.ConversationID]
RETURNS [nb: NB, cDesc: ConvDesc, sCDesc: FinchSynthesizer.SConvDesc] = {
[nb, cDesc] ← FinchSmarts.ServiceConnect["text-to-speech", convID];
IF nb#$success THEN RETURN;
sCDesc ← GetSCDescE[cDesc];
nb ← SynthesizerInterface[cDesc, sCDesc];
};
GetSCDescE: ENTRY PROC[
cDesc: ConvDesc ] RETURNS [sCDesc: FinchSynthesizer.SConvDesc] = {
ENABLE UNWIND => NULL;
IF cDesc=NIL THEN ERROR;
RETURN[GetSCDesc[cDesc, TRUE]];
};
GetSCDesc: INTERNAL PROC[
cDesc: ConvDesc, createOK: BOOLFALSE ] RETURNS [sCDesc: FinchSynthesizer.SConvDesc] = {
IF cDesc=NIL THEN RETURN[NIL];
sCDesc ← NARROW[Atom.GetPropFromList[cDesc.props, $SConvDesc]];
IF ~createOK OR sCDesc#NIL THEN RETURN;
sCDesc ← NEW[FinchSynthesizer.SConvDescBody ← []];
cDesc.props ← Atom.PutPropOnList[cDesc.props, $SConvDesc, sCDesc];
};
SynthesizerInterface: PROC[cDesc: ConvDesc, sCDesc: FinchSynthesizer.SConvDesc]
RETURNS [nb: NB←$success] = {
sCDesc=NIL if we're only interested in importing the interface.
interfaceSpec: Thrush.InterfaceSpec;
IF synthesizerServer#NIL AND (sCDesc=NIL OR sCDesc.synthesizerServiceID#nullID)
THEN RETURN;
[nb, shhh, interfaceSpec] ← FinchSmarts.ObtainServiceInterface[NIL, "SynthesizerServer", cDesc];
IF nb#$success THEN RETURN;
IF synthesizerServer=NIL THEN
synthesizerServer ← SynthesizerServerRpcControl.ImportNewInterface[
interfaceName: interfaceSpec.interfaceName,
hostHint: interfaceSpec.hostHint!RPC.ImportFailed => CONTINUE
];
IF synthesizerServer=NIL THEN RETURN[$noSynthesizerServerInterface];
IF sCDesc#NIL THEN sCDesc.synthesizerServiceID ← interfaceSpec.serviceID;
};
NewID: PROC RETURNS[LONG CARDINAL] ={RETURN[LOOPHOLE[PupSocket.GetUniqueID[]]]};
Initialization
InitializeFinchSynthesizer: PUBLIC PROC = {
FinchSmarts.RegisterForReports[s: NIL, c: ConvStateReport, r: ReportReport, before: TRUE];
Ask Finch to inform us of changes in conversation state, and action reports.
};
}.
Swinehart, October 3, 1986 4:40:54 pm PDT
Based on a prototype FinchRecordingImpl, and on code extracted from old FinchSmartsImpls., DIRECTORY, }, PlaybackTune