FinchSynthesizerImpl.mesa
Copyright Ó 1986, 1988 by Xerox Corporation. All rights reserved.
Last Edited by: Swinehart, September 22, 1990 3:18 pm PDT
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 = {
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;
};
defaultTranslateProc: FinchSynthesizer.SynthesizerTranslateProc ← NIL;
Hints, valid if serviceID is current.
synthesizerServer: SynthesizerServerRpcControl.InterfaceRecord ← NIL;
shhh: SHHH;
serviceID: RefID.ID ← nullID;
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: 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;
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] = {
Describe the assigned synthesizer.
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;
};
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: BOOLFALSE ] 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;
Count on validity of conversation to validate serviceID;
[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]]]};
Initialization
InitializeFinchSynthesizer: PUBLIC PROC = {
FinchSmarts.RegisterForReports[key: $FinchSynthesizer, s: NIL, c: ConvStateReport, r: ReportReport];
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