<> <> <> 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]; <> }; }. <<>> <> <> <<>> <<>> <<>>