FinchSynthesizerImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Swinehart, November 9, 1986 9:13:32 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;
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: 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 = {
ENABLE UNWIND=>NULL;
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 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];
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] ={
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