FinchSynthesizerImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Swinehart, November 3, 1986 7:45:01 am PST
DIRECTORY
Atom USING [ GetPropFromList, PutPropOnList ],
FinchSynthesizer,
FinchSmarts USING [ ConvDesc, LookupServiceInterface, RegisterForReports, ReportConversationStateProc, ReportRequestStateProc, VoiceConnect ],
GList USING [ Nconc ],
PupSocket USING [ GetUniqueID ],
RefID USING [ ID ],
RPC USING [ 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 GList, 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 { RETURN[NARROW[GList.Nconc[l1, l2]]]; };
synthesizerServer: SynthesizerServerRpcControl.InterfaceRecord ← NIL;
defaultTranslateProc: FinchSynthesizer.SynthesizerTranslateProc ← NIL;
shhh: SHHH;
Supervision
ConvStateReport: ENTRY FinchSmarts.ReportConversationStateProc = {
[nb: NB, cDesc: ConvDesc, remark: ROPE]
sCDesc: FinchSynthesizer.SConvDesc = GetSCDesc[cDesc];
IF sCDesc=NIL THEN ERROR;
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;
sCDesc: FinchSynthesizer.SConvDesc = GetSCDesc[cDesc];
IF sCDesc=NIL THEN ERROR;
BROADCAST sCDesc.stateChange;
SELECT actionReport.actionClass FROM
$synthesizer => {
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;
NoteScheduledSpeech: ENTRY PROC = {
ENABLE UNWIND=>NULL;
sCDesc.pendingSpecs ← NconcSpecs[sCDesc.pendingSpecs, LIST[synthSpec]]; };
[nb,cDesc,sCDesc]←SynthesizerConnect[convID];
IF nb#$success THEN RETURN; -- Complain?
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 ← synthesizerServer.TextToSpeech[
shhh, cDesc.situation.self, sCDesc.synthesizerServiceID, synthSpec.textToSpeak, synthSpec.actionID, TRUE, queueIt];
IF nb=$success THEN NoteScheduledSpeech[];
};
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] ={
cDesc: ConvDesc;
sCDesc: FinchSynthesizer.SConvDesc;
IF ([,cDesc, sCDesc]←SynthesizerConnect[convID]).nb#$success OR cDesc.situation.self.state#$active THEN RETURN;
[] ← 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 UNWIND => NULL;
cDesc: ConvDesc;
sCDesc: FinchSynthesizer.SConvDesc;
IF ([,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.VoiceConnect["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;
RETURN[GetSCDesc[cDesc]];
};
GetSCDesc: INTERNAL PROC[
cDesc: ConvDesc ] RETURNS [sCDesc: FinchSynthesizer.SConvDesc] = {
IF cDesc=NIL THEN RETURN[NIL];
sCDesc ← NARROW[Atom.GetPropFromList[cDesc.props, $SConvDesc]];
IF 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 sCDesc#NIL THEN { IF sCDesc.synthesizerServiceID#nullID THEN RETURN }
ELSE IF synthesizerServer#NIL THEN RETURN; -- want interface only
[nb, shhh, interfaceSpec] ← FinchSmarts.LookupServiceInterface["text-to-speech", "SynthesizerServer", cDesc];
IF nb#$success THEN RETURN;
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