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;
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: BOOL←TRUE,
synthesizerTranslateProc: FinchSynthesizer.SynthesizerTranslateProc←NIL,
synthesizerModel: ATOM←NIL, -- Not presently interpreted
synthesizerVersion: ATOM←NIL
]
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: BOOL←FALSE ] 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[]]]};
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