<<>> <> <> <> DIRECTORY Atom USING [ GetPropFromList, PutPropOnList ], FinchSynthesizer, FinchSmarts USING [ ConvDesc, ObtainServiceInterface, RegisterForReports, ReportConversationStateProc, ReportRequestStateProc, ReportSystemStateProc, ServiceConnect ], Random USING [ Create, NextInt, RandomStream ], RefID USING [ ID ], Rope USING [ ROPE ], Thrush USING [ ConversationID, InterfaceSpec, NB, nullConvID, nullID, SHHH ], Synthesizer USING [ ActionID, nullActionID, SynthSpec, SynthSpecBody, SynthSpecs ], SynthesizerServerRpcControl USING [InterfaceRecord ], SynthesizerServerSunImport USING [ ImportInterface ] ; FinchSynthesizerImpl: CEDAR MONITOR <<Confusion about locks here and in FinchSmartsImpl; careful!>> IMPORTS Random, SynthesizerServerSunImport, Atom, FinchSmarts EXPORTS FinchSynthesizer = { <> 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; }; defaultTranslateProc: FinchSynthesizer.SynthesizerTranslateProc ¬ NIL; <> synthesizerServer: SynthesizerServerRpcControl.InterfaceRecord ¬ NIL; shhh: SHHH; serviceID: RefID.ID ¬ nullID; <> <<>> SysStateReport: ENTRY FinchSmarts.ReportSystemStateProc = { <> }; 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; <> }; ReportReport: ENTRY FinchSmarts.ReportRequestStateProc = { <<[cDesc: ConvDesc, actionReport: Thrush.ActionReport, actionRequest: REF]>> <> <> ENABLE UNWIND => NULL; SELECT actionReport.actionClass FROM $synthesizer => { sCDesc: FinchSynthesizer.SConvDesc = GetSCDesc[cDesc]; IF sCDesc=NIL THEN RETURN; -- presumably nothing was pending! <> 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 <> $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 <> }; <> <<>> 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; nb ¬ sCDesc.synthInterface.clientStubTextToSpeech[ sCDesc.synthInterface, sCDesc.synthShhh, 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[]; }; }; SpeakText: PUBLIC PROC[textToSpeak: Rope.ROPE] RETURNS [nb: NB] = { RETURN[TextToSpeech[ synthSpec: NEW[Synthesizer.SynthSpecBody ¬ [textToSpeak: textToSpeak]]].nb]; }; StopSpeech: PUBLIC PROC -- TAKES -- [ convID: Thrush.ConversationID¬Thrush.nullConvID, actionID: Synthesizer.ActionID ¬ Synthesizer.nullActionID ] RETURNS [ nb: Thrush.NB¬$success, newConvID: Thrush.ConversationID, newActionID: Synthesizer.ActionID ] = { [nb, newConvID, newActionID] ¬ TextToSpeech[ convID, NEW[Synthesizer.SynthSpecBody ¬ [textToSpeak: NIL, actionID: actionID]], 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] ={ cDesc: ConvDesc; sCDesc: FinchSynthesizer.SConvDesc; IF ([nb,cDesc, sCDesc]¬SynthesizerConnect[convID]).nb#$success OR cDesc.situation.self.state#$active THEN RETURN; nb ¬ sCDesc.synthInterface.clientStubStopSpeech[ sCDesc.synthInterface, sCDesc.synthShhh, cDesc.situation.self, sCDesc.synthesizerServiceID, reset]; }; GetSynthesizerSpecifications: PUBLIC PROC[conversationID: Thrush.ConversationID] RETURNS [nb: Thrush.NB¬$success, synthesizerModel: ATOM, synthesizerVersion: ATOM] = { <> cDesc: ConvDesc; sCDesc: FinchSynthesizer.SConvDesc; IF ([nb,cDesc, sCDesc]¬SynthesizerConnect[conversationID]).nb#$success OR cDesc.situation.self.state#$active THEN RETURN; [nb, synthesizerModel, synthesizerVersion] ¬ sCDesc.synthInterface.clientStubGetSynthesizerSpecifications[sCDesc.synthInterface, sCDesc.synthShhh, sCDesc.synthesizerServiceID]; }; <<>> RegisterTranslateProc: PUBLIC PROC [ conversationID: Thrush.ConversationID, translate: FinchSynthesizer.SynthesizerTranslateProc¬NIL] = { defaultTranslateProc ¬ translate; }; <<>> <> 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] = { interfaceSpec: Thrush.InterfaceSpec; IF cDesc=NIL OR sCDesc = NIL THEN ERROR; -- we don't support this mode IF synthesizerServer#NIL AND sCDesc.synthesizerServiceID#nullID THEN RETURN; <> [nb, interfaceSpec] ¬ FinchSmarts.ObtainServiceInterface[NIL, "SynthesizerServer", cDesc]; IF nb#$success THEN RETURN; IF synthesizerServer=NIL OR interfaceSpec.serviceID#serviceID THEN { [synthesizerServer, shhh] ¬ SynthesizerServerSunImport.ImportInterface[ instance: interfaceSpec.interfaceName.instance]; serviceID ¬ interfaceSpec.serviceID; }; sCDesc.synthesizerServiceID ¬ serviceID; sCDesc.synthShhh ¬ shhh; sCDesc.synthInterface ¬ synthesizerServer; }; idStream: Random.RandomStream ¬ Random.Create[seed: -1]; NewID: PROC RETURNS[CARD]~{RETURN[LOOPHOLE[Random.NextInt[idStream]]]}; <> InitializeFinchSynthesizer: PUBLIC PROC = { FinchSmarts.RegisterForReports[key: $FinchSynthesizer, s: NIL, c: ConvStateReport, r: ReportReport]; <> }; }. <<>> <> <> <<>> <<>> <<>>